package k8s

import (
	"fmt"
	"net/http"
	"net/http/httptest"
	"net/url"
	"path"
	"testing"

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

func TestGenerateImportEndpointURL(t *testing.T) {
	clusterID := "888"
	importID := "999"
	require.Equal(t,
		fmt.Sprintf("http://172.172.172.172/proxy/cluster/%s/v1alpha1/import/%s", clusterID, importID),
		generateImportEndpointURL("172.172.172.172", clusterID, importID),
	)
}

func TestHTTPLinkByClusterProxyBasic(t *testing.T) {
	mux := http.NewServeMux()
	const (
		clusterID = "888"
		importID  = "999"
		authToken = "SECRET_TOKEN"
	)
	mux.HandleFunc(path.Join(generateImportEndpointPath(clusterID, importID), "/progress/task"), func(w http.ResponseWriter, r *http.Request) {
		switch r.Method {
		case http.MethodGet:
			w.WriteHeader(http.StatusOK)
			_, err := w.Write([]byte(fmt.Sprintf("task progress for %s", importID)))
			require.NoError(t, err)
		default:
			http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
		}
	})
	mux.HandleFunc(path.Join(generateImportEndpointPath(clusterID, importID), "/task"), func(w http.ResponseWriter, r *http.Request) {
		switch r.Method {
		case http.MethodGet:
			w.WriteHeader(http.StatusOK)
			_, err := w.Write([]byte("current import tasks"))
			require.NoError(t, err)
		case http.MethodPost:
			w.WriteHeader(http.StatusOK)
			_, err := w.Write([]byte("new import task registered"))
			require.NoError(t, err)
		default:
			http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
		}
	})
	mux.HandleFunc("/", http.NotFound)
	testServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		token := r.Header.Get("Authorization")
		if token != fmt.Sprintf("bearer %s", authToken) {
			http.Error(w, "Unauthorized", http.StatusUnauthorized)
			return
		}
		mux.ServeHTTP(w, r)
	}))
	testServerURL, err := url.Parse(testServer.URL)
	require.NoError(t, err)
	httpLink := NewHTTPLinkByClusterProxy(testServerURL.Host, clusterID, importID,
		func(req *http.Request) error {
			req.Header.Set("Authorization", fmt.Sprintf("bearer %s", authToken))
			return nil
		},
	)
	testCases := []struct {
		CaseName         string
		Method           string
		SubPath          string
		BodyData         interface{}
		ExpectedResponse string
		ExpectedErr      string
	}{
		{
			CaseName:         "normal progress get",
			Method:           http.MethodGet,
			SubPath:          "/progress/task",
			BodyData:         nil,
			ExpectedResponse: "task progress for 999",
			ExpectedErr:      "",
		},
		{
			CaseName:         "normal task post",
			Method:           http.MethodPost,
			SubPath:          "/task",
			BodyData:         "task data",
			ExpectedResponse: "new import task registered",
			ExpectedErr:      "",
		},
		{
			CaseName:         "normal task get",
			Method:           http.MethodGet,
			SubPath:          "/task",
			BodyData:         nil,
			ExpectedResponse: "current import tasks",
			ExpectedErr:      "",
		},
		{
			CaseName:         "invalid path",
			Method:           http.MethodGet,
			SubPath:          "/non-existing",
			BodyData:         nil,
			ExpectedResponse: "",
			ExpectedErr:      "get http response body error, status code: 404, body: 404 page not found\n",
		},
	}
	for _, tc := range testCases {
		t.Run(tc.CaseName, func(t *testing.T) {
			var (
				response []byte
				err      error
			)

			switch tc.Method {
			case http.MethodGet:
				response, err = httpLink.Get(tc.SubPath)
			case http.MethodPost:
				response, err = httpLink.Post(tc.SubPath, tc.BodyData)
			default:
				require.Fail(t, "unsupported method")
			}
			if len(tc.ExpectedErr) > 0 {
				require.Equal(t, tc.ExpectedErr, err.Error())
			} else {
				require.NoError(t, err)
				require.Equal(t, tc.ExpectedResponse, string(response))
			}
		})
	}
}
