package k8s

import (
	"context"
	"io"
	"net/http"
	"net/http/httptest"
	"net/url"
	"path"
	"strings"
	"testing"
	"time"

	. "github.com/onsi/gomega"
	"github.com/stretchr/testify/require"
)

func TestURLJoint(t *testing.T) {
	g := NewGomegaWithT(t)
	base := "https://a.b.csv"
	apiPath := "/as/ads/asd"
	url, err := URLJoint(base, apiPath)
	g.Expect(err).To(BeNil())
	t.Logf("%s", url)
	g.Expect(url).To(Equal(base + apiPath))
}

func TestGetHTTPResponseBody(t *testing.T) {
	mux := http.NewServeMux()
	mux.HandleFunc("/test", func(w http.ResponseWriter, r *http.Request) {
		switch r.Method {
		case http.MethodGet:
			w.WriteHeader(http.StatusOK)
			_, err := w.Write([]byte("hello world"))
			require.NoError(t, err)
		case http.MethodPost:
			w.WriteHeader(http.StatusOK)
			_, err := io.Copy(w, r.Body)
			require.NoError(t, err)
		default:
			http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
		}
	})
	mux.HandleFunc("/private/test", func(w http.ResponseWriter, r *http.Request) {
		token := r.Header.Get("Authorization")
		if token != "SECRET_TOKEN" {
			http.Error(w, "Unauthorized", http.StatusUnauthorized)
			return
		}
		switch r.Method {
		case http.MethodGet:
			w.WriteHeader(http.StatusOK)
			_, err := w.Write([]byte("private hello world"))
			require.NoError(t, err)
		default:
			http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
		}
	})
	mux.HandleFunc("/", http.NotFound)
	testServer := httptest.NewServer(mux)
	genURL := func(apiPath string) string {
		fullURL, err := url.Parse(testServer.URL)
		require.NoError(t, err)
		fullURL.Path = path.Join(fullURL.Path, apiPath)
		return fullURL.String()
	}
	testURL := genURL("/test")
	nonExistingURL := genURL("/non-existing")
	privateURL := genURL("/private/test")
	testCases := []struct {
		CaseName         string
		Method           string
		URL              string
		ReqDecos         []RequestDecorator
		Body             io.Reader
		ExpectStatusCode int
		ExpectBody       string
	}{
		{
			CaseName:         "normal get",
			Method:           http.MethodGet,
			URL:              testURL,
			ReqDecos:         nil,
			Body:             nil,
			ExpectStatusCode: http.StatusOK,
			ExpectBody:       "hello world",
		},
		{
			CaseName:         "normal post",
			Method:           http.MethodPost,
			URL:              testURL,
			ReqDecos:         nil,
			Body:             strings.NewReader("hello world from post"),
			ExpectStatusCode: http.StatusOK,
			ExpectBody:       "hello world from post",
		},
		{
			CaseName:         "not allowed method",
			Method:           http.MethodPut,
			URL:              testURL,
			ReqDecos:         nil,
			Body:             strings.NewReader("hello world from post"),
			ExpectStatusCode: http.StatusMethodNotAllowed,
			ExpectBody:       "Method not allowed\n",
		},
		{
			CaseName:         "no existing URL",
			Method:           http.MethodGet,
			URL:              nonExistingURL,
			ReqDecos:         nil,
			Body:             nil,
			ExpectStatusCode: http.StatusNotFound,
			ExpectBody:       "404 page not found\n",
		},
		{
			CaseName:         "missing extra header",
			Method:           http.MethodGet,
			URL:              privateURL,
			ReqDecos:         nil,
			Body:             nil,
			ExpectStatusCode: http.StatusUnauthorized,
			ExpectBody:       "Unauthorized\n",
		},
		{
			CaseName: "with extra header",
			Method:   http.MethodGet,
			URL:      privateURL,
			ReqDecos: []RequestDecorator{
				func(req *http.Request) error {
					req.Header.Set("Authorization", "SECRET_TOKEN")
					return nil
				},
			},
			Body:             nil,
			ExpectStatusCode: http.StatusOK,
			ExpectBody:       "private hello world",
		},
	}
	for _, tc := range testCases {
		t.Run(tc.CaseName, func(t *testing.T) {
			ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
			defer cancel()
			httpCli := testServer.Client()
			t.Logf("request URL: %+v", tc.URL)
			response, statusCode, err := getHTTPResponseBody(ctx, httpCli, tc.Method, tc.URL, tc.Body, tc.ReqDecos...)
			require.NoError(t, err)
			require.Equal(t, tc.ExpectStatusCode, statusCode)
			require.Equal(t, tc.ExpectBody, string(response))
		})
	}
}
