package influxdb_test

import (
	"bytes"
	"context"
	"fmt"
	"io"
	"io/ioutil"
	"net/http"
	"testing"
	"time"

	"github.com/google/go-cmp/cmp"
	"github.com/influxdata/flux/dependencies/dependenciestest"
	"github.com/influxdata/flux/dependencies/influxdb"
	protocol "github.com/influxdata/line-protocol"
)

type RoundTrip struct {
	RequestValidator      func(_ *http.Request) error
	RequestValidatorError error

	Bodies bytes.Buffer
}

func (f *RoundTrip) RoundTrip(req *http.Request) (*http.Response, error) {
	if f.RequestValidatorError == nil {
		f.RequestValidatorError = f.RequestValidator(req)
	}
	_, err := io.Copy(&f.Bodies, req.Body)
	if err != nil {
		panic(fmt.Sprintf("Error while copying request body: %s", err))
	}

	return &http.Response{
		StatusCode: 200,
		Status:     "Body generated by test client",

		// Send response to be tested
		Body: ioutil.NopCloser(new(bytes.Buffer)),

		// Must be set to non-nil value or it panics
		Header: make(http.Header),
	}, nil
}

func cpuMetric(usage float64, ns int) protocol.Metric {
	tags := map[string]string{"host": "localhost", "id": "cpua"}
	tm := time.Date(2017, 11, 17, 0, 0, 0, ns, time.UTC)
	met, err := protocol.New("cpu", tags, nil, tm)
	if err != nil {
		panic(fmt.Sprintf("error creating metric: %s", err))
	}
	met.AddField("usage_user", usage)
	met.AddField("log", "message")
	return met
}

func diskMetric(usage float64, ns int) protocol.Metric {
	tags := map[string]string{"id": "/dev/sdb"}
	tm := time.Date(2017, 11, 17, 0, 0, 0, ns, time.UTC)
	met, err := protocol.New("disk", tags, nil, tm)
	if err != nil {
		panic(fmt.Sprintf("error creating metric: %s", err))
	}
	met.AddField("usage_disk", usage)
	met.AddField("log", "disk message")
	return met
}

func Test_httpWriter_Write(t *testing.T) {
	tests := []struct {
		name     string
		metric   [][]protocol.Metric
		wantBody string
	}{
		{
			name: "basic",
			metric: [][]protocol.Metric{
				{
					cpuMetric(95, 1),
					cpuMetric(96, 2),
					cpuMetric(97, 3),
					cpuMetric(95, 4),
				},
				{
					diskMetric(45, 1),
					diskMetric(46, 2),
					diskMetric(47, 3),
					diskMetric(45, 4),
				},
			},
			wantBody: `cpu,host=localhost,id=cpua usage_user=95,log="message" 1510876800000000001
cpu,host=localhost,id=cpua usage_user=96,log="message" 1510876800000000002
cpu,host=localhost,id=cpua usage_user=97,log="message" 1510876800000000003
cpu,host=localhost,id=cpua usage_user=95,log="message" 1510876800000000004
disk,id=/dev/sdb usage_disk=45,log="disk message" 1510876800000000001
disk,id=/dev/sdb usage_disk=46,log="disk message" 1510876800000000002
disk,id=/dev/sdb usage_disk=47,log="disk message" 1510876800000000003
disk,id=/dev/sdb usage_disk=45,log="disk message" 1510876800000000004
`,
		},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			h := influxdb.HttpProvider{
				DefaultConfig: influxdb.Config{
					Host:  "http://myhost.com:8085",
					Token: "mytoken",
				},
			}
			deps := dependenciestest.Default()
			roundTripper := &RoundTrip{
				RequestValidator: func(req *http.Request) error {
					url := req.URL
					values := url.Query()
					if val, exp := req.Header.Get("Authorization"), "Token mytoken"; val != exp {
						return fmt.Errorf("token does not match, expected %s, got %s", exp, val)
					}
					if val, exp := values.Get("bucket"), "mybucket"; val != exp {
						return fmt.Errorf("bucket does not match, expected %s, got %s", exp, val)
					}
					if val, exp := values.Get("org"), "myorg"; val != exp {
						return fmt.Errorf("org does not match, expected %s, got %s", exp, val)
					}
					if val, exp := url.Host, "myhost.com:8085"; val != exp {
						return fmt.Errorf("host does not match, expected %s, got %s", exp, val)
					}
					if val, exp := url.Path, "/api/v2/write"; val != exp {
						return fmt.Errorf("path does not match, expected %s, got %s", exp, val)
					}
					return nil
				},
			}
			deps.Deps.HTTPClient = &http.Client{
				Transport: roundTripper,
			}
			ctx := deps.Inject(context.Background())
			writer, err := h.WriterFor(ctx, influxdb.Config{
				Org:    influxdb.NameOrID{Name: "myorg"},
				Bucket: influxdb.NameOrID{Name: "mybucket"},
			})
			if err != nil {
				t.Errorf("WriterFor() error = %v", err)
			}
			for i := range tt.metric {
				if err := writer.Write(tt.metric[i]...); err != nil {
					t.Errorf("Write() error = %v", err)
				}
			}
			writer.Close()
			if roundTripper.RequestValidatorError != nil {
				t.Errorf("Query validation error = %v", roundTripper.RequestValidatorError)
			}
			if roundTripper.Bodies.String() != tt.wantBody {
				t.Errorf(cmp.Diff(roundTripper.Bodies.String(), tt.wantBody))
			}
		})
	}
}
