//go:build integration && perftest
// +build integration,perftest

package main

import (
	"flag"
	"fmt"
	"net/http"
	"strings"
	"time"
)

type Config struct {
	RequestDuration time.Duration
	RequestCount    int64
	RequestDelay    time.Duration

	Endpoint    string
	Bucket, Key string

	SDK    SDKConfig
	Client ClientConfig
}

func (c *Config) SetupFlags(prefix string, flagset *flag.FlagSet) {
	flagset.DurationVar(&c.RequestDuration, "duration", 0,
		"The duration to make requests for. Use instead of count for specific running duration.")
	flagset.Int64Var(&c.RequestCount, "count", 0,
		"The total `number` of requests to make. Use instead of duration for specific count.")
	flagset.DurationVar(&c.RequestDelay, "delay", 0,
		"The detail between sequential requests.")
	flagset.StringVar(&c.Endpoint, prefix+"endpoint", "",
		"Optional overridden endpoint S3 client will connect to.")
	flagset.StringVar(&c.Bucket, "bucket", "",
		"The S3 bucket `name` to request the object from.")
	flagset.StringVar(&c.Key, "key", "",
		"The S3 object key `name` to request the object from.")

	c.SDK.SetupFlags(prefix, flagset)
	c.Client.SetupFlags(prefix, flagset)
}

func (c *Config) Validate() error {
	var errs Errors

	if c.RequestDuration != 0 && c.RequestCount != 0 {
		errs = append(errs, fmt.Errorf("duration and count canot be used together"))
	}
	if c.RequestDuration == 0 && c.RequestCount == 0 {
		errs = append(errs, fmt.Errorf("duration or count must be provided"))
	}
	if len(c.Bucket) == 0 || len(c.Key) == 0 {
		errs = append(errs, fmt.Errorf("bucket and key are required"))
	}

	if err := c.SDK.Validate(); err != nil {
		errs = append(errs, err)
	}
	if err := c.Client.Validate(); err != nil {
		errs = append(errs, err)
	}

	if len(errs) != 0 {
		return errs
	}

	return nil
}

type SDKConfig struct {
	Anonymous      bool
	ExpectContinue bool
}

func (c *SDKConfig) SetupFlags(prefix string, flagset *flag.FlagSet) {
	prefix += "sdk."

	flagset.BoolVar(&c.Anonymous, prefix+"anonymous", false,
		"Specifies if the SDK will make requests anonymously, and unsigned.")

	c.ExpectContinue = true
	//	flagset.BoolVar(&c.ExpectContinue, prefix+"100-continue", true,
	//		"Specifies if the SDK requests will wait for the 100 continue response before sending request payload.")
}

func (c *SDKConfig) Validate() error {
	return nil
}

type ClientConfig struct {
	KeepAlive bool
	Timeouts  Timeouts
}

func (c *ClientConfig) SetupFlags(prefix string, flagset *flag.FlagSet) {
	prefix += "client."

	flagset.BoolVar(&c.KeepAlive, prefix+"http-keep-alive", true,
		"Specifies if HTTP keep alive is enabled.")

	c.Timeouts.SetupFlags(prefix, flagset)
}

func (c *ClientConfig) Validate() error {
	var errs Errors

	if err := c.Timeouts.Validate(); err != nil {
		errs = append(errs, err)
	}

	if len(errs) != 0 {
		return errs
	}
	return nil
}

type Timeouts struct {
	Connect        time.Duration
	TLSHandshake   time.Duration
	ExpectContinue time.Duration
	ResponseHeader time.Duration
}

func (c *Timeouts) SetupFlags(prefix string, flagset *flag.FlagSet) {
	prefix += "timeout."

	flagset.DurationVar(&c.Connect, prefix+"connect", 30*time.Second,
		"The `timeout` connecting to the remote host.")

	defTR := http.DefaultTransport.(*http.Transport)

	flagset.DurationVar(&c.TLSHandshake, prefix+"tls", defTR.TLSHandshakeTimeout,
		"The `timeout` waiting for the TLS handshake to complete.")

	c.ExpectContinue = defTR.ExpectContinueTimeout
	//	flagset.DurationVar(&c.ExpectContinue, prefix+"expect-continue", defTR.ExpectContinueTimeout,
	//		"The `timeout` waiting for the TLS handshake to complete.")

	flagset.DurationVar(&c.ResponseHeader, prefix+"response-header", defTR.ResponseHeaderTimeout,
		"The `timeout` waiting for the TLS handshake to complete.")
}

func (c *Timeouts) Validate() error {
	return nil
}

type Errors []error

func (es Errors) Error() string {
	var buf strings.Builder
	for _, e := range es {
		buf.WriteString(e.Error())
	}

	return buf.String()
}
