package testutils

import (
	"bytes"
	"context"
	"io/ioutil"
	"net"
	"net/http"
	"net/url"
	"testing"

	"github.com/stretchr/testify/assert"
	"github.com/valyala/fasthttp"
	"github.com/valyala/fasthttp/fasthttputil"
)

type mockRequestBody struct {
	*bytes.Buffer
}

func (m *mockRequestBody) Close() error {
	m.Reset()
	return nil
}

func NewMockRequest(uri string, body string) *http.Request {
	req := &http.Request{}

	if uri == "" {
		uri = "http://foo.bar/"
	}

	mUrl, err := url.ParseRequestURI(uri)
	if err != nil {
		panic(err)
	}
	req.URL = mUrl

	if body != "" {
		req.Body = &mockRequestBody{bytes.NewBufferString(body)}
	}

	return req
}

// see https://github.com/valyala/fasthttp/issues/36#issuecomment-447593252
func MockServe(request *http.Request, handler fasthttp.RequestHandler) (*http.Response, error) {
	ln := fasthttputil.NewInmemoryListener()
	defer func() {
		if err := ln.Close(); err != nil {
			panic(err)
		}
	}()

	go func() {
		err := fasthttp.Serve(ln, handler)
		if err != nil {
			panic(err)
		}
	}()

	client := http.Client{
		Transport: &http.Transport{
			DialContext: func(ctx context.Context, network, addr string) (net.Conn, error) {
				return ln.Dial()
			},
		},
	}
	return client.Do(request)
}

func AssertMockServe(t *testing.T, request *http.Request, handler fasthttp.RequestHandler) *http.Response {
	res, err := MockServe(request, handler)
	if !assert.Nilf(t, err, "failed to get response %s", err) {
		panic(nil)
	}
	return res
}

func AssertResponse(t *testing.T, res *http.Response, expectedStatus int, expectedBody string) {
	AssertResponseStatus(t, res, expectedStatus)
	AssertResponseBody(t, res, expectedBody)
}

func AssertResponseBody(t *testing.T, res *http.Response, excepted string) {
	body, err := ioutil.ReadAll(res.Body)
	if err != nil {
		t.Errorf("failed to read body, %s", err)
		t.FailNow()
	}
	assert.Equal(t, excepted, string(body), "response body mismatched")
}

func AssertResponseStatus(t *testing.T, res *http.Response, expected int) {
	assert.Equal(t, expected, res.StatusCode, "response status mismatched")
}
