package main

import (
	"context"
	"crypto/tls"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"math/rand"
	"net/http"
	"net/http/httptrace"
	"os"
	"strconv"
	"time"

	"github.com/google/uuid"
	"github.com/minio/minio-go/v7"
	"github.com/minio/minio-go/v7/pkg/credentials"

	"github.com/aws/aws-sdk-go-v2/aws"
	"github.com/aws/aws-sdk-go-v2/config"
	"github.com/aws/aws-sdk-go-v2/service/s3"
)

type PRNG struct {
	rem int64
}

func (r *PRNG) Read(p []byte) (n int, err error) {
	//log.Printf("rem=%d, buf=%d\n", r.rem, len(p))
	if int64(len(p)) > r.rem {
		p = p[:r.rem]
	}

	if int64(len(p)) > r.rem {
		log.Fatal("LOGIC ERROR")
	}

	n, err = rand.Read(p)
	if err != nil {
		return
	}
	r.rem -= int64(n)
	if r.rem <= 0 {
		err = io.EOF
		//log.Printf("PRNG file has been fully read. rem=%d,n=%d,err=%s\n", r.rem, n, err)
	}
	return
}

func main() {
	var err error
	fmt.Printf("nanoseconds\n")

	// Initial setup
	_, isSSL := os.LookupEnv("SSL")
	opts := minio.Options{
		Creds:  credentials.NewStaticV4(os.Getenv("AWS_ACCESS_KEY_ID"), os.Getenv("AWS_SECRET_ACCESS_KEY"), ""),
		Secure: isSSL,
	}

	if region, ok := os.LookupEnv("AWS_REGION"); ok {
		opts.Region = region
	}

	if _, ok := os.LookupEnv("SSL_INSECURE"); ok {
		opts.Transport = &http.Transport{TLSClientConfig: &tls.Config{InsecureSkipVerify: true}}
	}

	size := 1 * 1024 * 1024 // 1 MB
	if env_size, ok := os.LookupEnv("SIZE"); ok {
		size, err = strconv.Atoi(env_size)
		if err != nil {
			log.Fatal(err)
			return
		}
	}

	repeat := 60
	if env_repeat, ok := os.LookupEnv("REPEAT"); ok {
		repeat, err = strconv.Atoi(env_repeat)
		if err != nil {
			log.Fatal(err)
			return
		}
	}

	mc, err := minio.New(os.Getenv("ENDPOINT"), &opts)
	if err != nil {
		log.Fatal("failed connect", err)
		return
	}

	// AWS SDK stuff
	customResolver := aws.EndpointResolverWithOptionsFunc(func(service, region string, options ...interface{}) (aws.Endpoint, error) {
        	return aws.Endpoint{
            		PartitionID:   "aws",
			URL:           "http://"+os.Getenv("ENDPOINT"),
            		SigningRegion: region,
        	}, nil
	})


	cfg, err := config.LoadDefaultConfig(context.TODO(), config.WithEndpointResolverWithOptions(customResolver))
	if err != nil {
		log.Fatalf("failed to load configuration, %v", err)
	}

	// Create an Amazon S3 service client
	client := s3.NewFromConfig(cfg, func(opts *s3.Options) {
		opts.UsePathStyle = true
	})


	// Create Bucket
	buck := uuid.New().String()
	err = mc.MakeBucket(context.Background(), buck, minio.MakeBucketOptions{})
	if err != nil {
		log.Fatal(err)
		return
	}

	// Send an object
	prng := new(PRNG)
	prng.rem = int64(size)


	key := uuid.New().String()
	_, err = mc.PutObject(
		context.Background(),
		buck,
		key,
		prng,
		int64(size),
		minio.PutObjectOptions{ContentType: "application/octet-stream"},
	)
	if err != nil {
		log.Fatal("unable to upload our test object")
		return
	}

	for i := 0; i < repeat; i++ {
		// Now, try to fetch it *repeat* times (assume no caching)
		var elapsed time.Duration

		var start time.Time
    		trace := &httptrace.ClientTrace{
			GotFirstResponseByte: func() { elapsed = time.Since(start) },
		}

		start = time.Now()
		/*object, err := mc.GetObject(httptrace.WithClientTrace(context.Background(), trace), buck, key, minio.GetObjectOptions{})
		if err != nil {
			log.Fatal(err)
			return
		}*/

		out, err := client.GetObject(httptrace.WithClientTrace(context.Background(), trace), &s3.GetObjectInput {
			Bucket: aws.String(buck),
			Key: aws.String(key),
		})
		if err != nil {
			log.Fatal(err)
			return
		}
		object := out.Body


		if _, err = io.Copy(ioutil.Discard, object) ; err != nil {
			log.Fatal("failed getobject: ", err)
			return
		}

		fmt.Printf("%v\n", elapsed.Nanoseconds())
		//time.Sleep(10 * time.Second)
	}
}

/*
func timeGet(url string) {
    req, _ := http.NewRequest("GET", url, nil)

    var start, connect, dns, tlsHandshake time.Time

    trace := &httptrace.ClientTrace{
        DNSStart: func(dsi httptrace.DNSStartInfo) { dns = time.Now() },
        DNSDone: func(ddi httptrace.DNSDoneInfo) {
            fmt.Printf("DNS Done: %v\n", time.Since(dns))
        },

        TLSHandshakeStart: func() { tlsHandshake = time.Now() },
        TLSHandshakeDone: func(cs tls.ConnectionState, err error) {
            fmt.Printf("TLS Handshake: %v\n", time.Since(tlsHandshake))
        },

        ConnectStart: func(network, addr string) { connect = time.Now() },
        ConnectDone: func(network, addr string, err error) {
            fmt.Printf("Connect time: %v\n", time.Since(connect))
        },

        GotFirstResponseByte: func() {
            fmt.Printf("Time from start to first byte: %v\n", time.Since(start))
        },
    }

    req = req.WithContext(httptrace.WithClientTrace(req.Context(), trace))
    start = time.Now()
    if _, err := http.DefaultTransport.RoundTrip(req); err != nil {
        log.Fatal(err)
    }
    fmt.Printf("Total time: %v\n", time.Since(start))
}
*/
