package clients

import (
	"bytes"
	"errors"
	"io"
	"net/http"
	"net/http/httptest"
	"testing"

	"github.com/AlchemyTelcoSolutions/xutils-go/xlogger"
	"github.com/stretchr/testify/assert"
)

type testHttp struct {
	TestTitle          string
	ProxyURL           string
	RequestBody        string
	RequestHeaders     map[string]string
	ExtraRequestHeader map[string]string
	DoFunc             func(req *http.Request) (*http.Response, error)
	IsDoProxy          bool
	IsSuccess          bool
	Expected           string
}

type MockHttpClient struct {
	DoFunc func(req *http.Request) (*http.Response, error)
}

func (m *MockHttpClient) Do(req *http.Request) (*http.Response, error) {
	return m.DoFunc(req)
}

func TestConfig(t *testing.T) {
	proxyUrl := "https://localhost:3000/v1/v2/v3"
	errorText := "Error from web server"
	stringOne := `{"name":"Test Name","full_name":"test full name"}`
	stringTwo := `{"name":"Test Two","full_name":"test full name Two"}`
	stringThree := `{"name":"Test Three","full_name":"test full name Three"}`
	stringFour := `{"name":"Test Four","full_name":"test full name Four"}`
	tests := []testHttp{
		{
			TestTitle:      "test Invalid Proxy Url Error Response",
			ProxyURL:       "h://3000.1.2.3.4.5?query=one&query2=two",
			RequestBody:    stringOne,
			RequestHeaders: nil,
			DoFunc: func(*http.Request) (*http.Response, error) {
				return nil, errors.New(errorText)
			},
			IsDoProxy: true,
			Expected:  "no scheme, host or path in the URL",
		},
		{
			TestTitle:      "test Invalid Proxy Url escape Error Response",
			ProxyURL:       "!@#$%^&*()://asdasd.com",
			RequestBody:    stringOne,
			RequestHeaders: nil,
			DoFunc: func(*http.Request) (*http.Response, error) {
				return nil, errors.New(errorText)
			},
			IsDoProxy: true,
			Expected:  `invalid URL escape`,
		},
		{
			TestTitle:      "test Send Error Response",
			ProxyURL:       proxyUrl,
			RequestBody:    stringOne,
			RequestHeaders: nil,
			DoFunc: func(*http.Request) (*http.Response, error) {
				return nil, errors.New(errorText)
			},
			Expected: errorText,
		},
		{
			TestTitle:          "test DoProxy Error Response",
			ProxyURL:           proxyUrl,
			RequestBody:        stringTwo,
			RequestHeaders:     map[string]string{"one": "two"},
			ExtraRequestHeader: map[string]string{"three": "four"},
			DoFunc: func(*http.Request) (*http.Response, error) {
				return nil, errors.New(errorText)
			},
			IsDoProxy: true,
			Expected:  errorText,
		},
		{
			TestTitle:   "test Do Success Response",
			ProxyURL:    proxyUrl,
			RequestBody: stringThree,
			DoFunc: func(*http.Request) (*http.Response, error) {
				r := io.NopCloser(bytes.NewReader([]byte(stringThree)))
				return &http.Response{
					StatusCode: 200,
					Body:       r,
				}, nil
			},
			IsSuccess: true,
			Expected:  stringThree,
		},
		{
			TestTitle:          "test DoProxy Success Response",
			ProxyURL:           proxyUrl,
			RequestBody:        stringFour,
			RequestHeaders:     map[string]string{"one": "two"},
			ExtraRequestHeader: map[string]string{"three": "four"},
			DoFunc: func(*http.Request) (*http.Response, error) {
				r := io.NopCloser(bytes.NewReader([]byte(stringFour)))
				return &http.Response{
					StatusCode: 200,
					Body:       r,
				}, nil
			},
			IsDoProxy: true,
			IsSuccess: true,
			Expected:  stringFour,
		},
	}
	for _, test := range tests {
		var response *http.Response
		var err error
		client := &MockHttpClient{test.DoFunc}
		logger := xlogger.NoOpLogger{}
		proxyClient := InitCallistoAPIHTTPClient(client, logger)
		request := getTestRequest(test.RequestBody, test.RequestHeaders)
		if test.IsDoProxy {
			response, err = proxyClient.DoProxy(request, test.ProxyURL, test.ExtraRequestHeader)
		} else {
			response, err = proxyClient.Send(request)
		}
		if !test.IsSuccess {
			assert.ErrorContains(t, err, test.Expected)
		} else {
			b, err := io.ReadAll(response.Body)
			if err != nil {
				t.Error("test Fail, error converting response")
			}
			assert.Equal(t, string(b), test.Expected)
		}
	}
}

func getTestRequest(jsonData string, headers map[string]string) *http.Request {
	body := io.NopCloser(bytes.NewReader([]byte(jsonData)))
	httpRequest := httptest.NewRequest(http.MethodPost, "http://localhost.com?with_query_string=1&and_second=2", body)
	for key, value := range headers {
		httpRequest.Header.Add(key, value)
	}
	return httpRequest
}
