package collection

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

	"github.com/aws/aws-sdk-go/aws/session"
	"github.com/aws/aws-sdk-go/service/s3"
	"go.opentelemetry.io/otel/trace"
)

// Contains all of the endpoint logic.

type response struct {
	TraceID string `json:"traceId"`
}

type s3Client struct {
	client *s3.S3
}

func NewS3Client() (*s3Client, error) {
	s, err := session.NewSession()
	if err != nil {
		return nil, err
	}
	svc := s3.New(s)
	return &s3Client{client: svc}, nil
}

// AwsSdkCall mocks a request to s3. ListBuckets are nil so no credentials are needed.
// Generates an Xray Trace ID.
func AwsSdkCall(w http.ResponseWriter, r *http.Request, rqmc *requestBasedMetricCollector, s3 *s3Client) {
	w.Header().Set("Content-Type", "application/json")

	s3.client.ListBuckets(nil) // nil or else would need real aws credentials

	ctx, span := tracer.Start(
		r.Context(),
		"aws-sdk-call",
		trace.WithAttributes(traceCommonLabels...),
	)
	defer span.End()

	// Request based metrics provided by rqmc
	rqmc.AddApiRequest()
	rqmc.UpdateTotalBytesSent(ctx)
	rqmc.UpdateLatencyTime(ctx)

	writeResponse(span, w)
}

// OutgoingSampleApp makes a request to another Sampleapp and generates an Xray Trace ID. It will also make a request to amazon.com.
func OutgoingSampleApp(w http.ResponseWriter, r *http.Request, client http.Client, rqmc *requestBasedMetricCollector) {

	ctx, span := tracer.Start(
		r.Context(),
		"invoke-sample-apps",
		trace.WithAttributes(traceCommonLabels...),
	)
	defer span.End()
	count := len(rqmc.config.SampleAppPorts)

	// If there are no sample app port list is empty then make a request to amazon.com (leaf request)
	if count == 0 {
		ctx, span := tracer.Start(
			ctx,
			"leaf-request",
			trace.WithAttributes(traceCommonLabels...),
		)

		req, _ := http.NewRequestWithContext(ctx, "GET", "https://aws.amazon.com", nil)
		res, err := client.Do(req)
		if err != nil {
			fmt.Println(err)
		}

		defer res.Body.Close()
		// Request based metrics provided by rqmc
		rqmc.AddApiRequest()
		rqmc.UpdateTotalBytesSent(ctx)
		rqmc.UpdateLatencyTime(ctx)

		span.End()

	} else { // If there are sample app ports to make a request to (chain request)
		invokeSampleApps(ctx, client, rqmc)
	}
	writeResponse(span, w)

}

// invokeSampleApps loops through the list of sample app ports provided in the configuration file and makes a call to invoke().
func invokeSampleApps(ctx context.Context, client http.Client, rqmc *requestBasedMetricCollector) {

	for _, port := range rqmc.config.SampleAppPorts {
		if port != "" {
			invoke(ctx, port, client)
		}
	}
}

// invoke uses the port given in the parameters to make an http request.
func invoke(ctx context.Context, port string, client http.Client) {

	ctx, span := tracer.Start(
		ctx,
		"invoke-sample-app",
		trace.WithAttributes(traceCommonLabels...),
	)
	// Consider making requests on other than localhost
	addr := "http://" + net.JoinHostPort("0.0.0.0", port) + "/outgoing-sampleapp"
	fmt.Println(addr)
	req, _ := http.NewRequestWithContext(ctx, "GET", addr, nil)
	res, err := client.Do(req)

	if err != nil {
		fmt.Println(err)
	}

	defer res.Body.Close()
	defer span.End()

}

// OutgoingHttpCall makes an HTTP GET request to https://aws.amazon.com/ and generates an Xray Trace ID.
func OutgoingHttpCall(w http.ResponseWriter, r *http.Request, client http.Client, rqmc *requestBasedMetricCollector) {

	w.Header().Set("Content-Type", "application/json")

	ctx, span := tracer.Start(
		r.Context(),
		"outgoing-http-call",
		trace.WithAttributes(traceCommonLabels...),
	)

	defer span.End()

	req, _ := http.NewRequestWithContext(ctx, "GET", "https://aws.amazon.com/", nil)
	res, err := client.Do(req)
	if err != nil {
		fmt.Println(err)
	}

	defer res.Body.Close()

	// Request based metrics provided by rqmc
	rqmc.AddApiRequest()
	rqmc.UpdateTotalBytesSent(ctx)
	rqmc.UpdateLatencyTime(ctx)
	writeResponse(span, w)

}

// getXrayTraceID generates a trace ID in Xray format from the span context.
func getXrayTraceID(span trace.Span) string {
	xrayTraceID := span.SpanContext().TraceID().String()
	return fmt.Sprintf("1-%s-%s", xrayTraceID[0:8], xrayTraceID[8:])
}

func writeResponse(span trace.Span, w http.ResponseWriter) {
	xrayTraceID := getXrayTraceID(span)
	payload, _ := json.Marshal(response{TraceID: xrayTraceID})
	w.Header().Set("Content-Type", "application/json")
	w.Write(payload)
}
