package tester

import (
	"encoding/json"
	"net/http"
	"net/http/httptest"
	"regexp"
	"testing"

	"github.com/jeremywohl/flatten"
	"github.com/julienschmidt/httprouter"

	th "github.com/fiskerinc/cloud-services/pkg/testhelper"
	"github.com/fiskerinc/cloud-services/pkg/validator"
)

type HttpTestCase struct {
	Request               *http.Request
	ExpectedStatus        int
	ExpectedResponse      string
	ExpectedRegexResponse string
	ExpectedRegexMap      map[string]string
	ValidateResponse      bool `default:"false"`
	Setup                 func()
}

func (tc *HttpTestCase) Test(handler http.HandlerFunc) *httptest.ResponseRecorder {
	return th.ExecHTTPHandler(handler, tc.Request)
}

func (tc *HttpTestCase) TestWithParamPath(handler http.HandlerFunc, routePath string) *httptest.ResponseRecorder {
	recorder := httptest.NewRecorder()

	router := httprouter.New()
	router.HandlerFunc(tc.Request.Method, routePath, handler)
	router.ServeHTTP(recorder, tc.Request)

	return recorder
}

func (tc *HttpTestCase) ValidateHttp(t *testing.T, name string, w *httptest.ResponseRecorder) {
	if w.Result().StatusCode != tc.ExpectedStatus {
		t.Errorf(th.TestErrorTemplate, name, tc.ExpectedStatus, w.Result().StatusCode)
	}

	if w.Body.String() != tc.ExpectedResponse && tc.ExpectedResponse != "" {
		t.Errorf(th.TestErrorTemplate, name, tc.ExpectedResponse, w.Body.String())
	}

	if tc.ValidateResponse {
		err := validator.ValidateStruct(w.Body)
		if err != nil {
			t.Error(err)
		}
	}

	if tc.ExpectedRegexResponse != "" {
		body := w.Body.String()
		match, _ := regexp.MatchString(tc.ExpectedRegexResponse, body)
		if !match {
			t.Errorf(th.TestErrorTemplate, name, tc.ExpectedRegexResponse, body)
		}
	}

	if tc.ExpectedRegexMap != nil {
		match, key, returned, _ := matchRegexMap(tc.ExpectedRegexMap, w)
		if !match {
			t.Errorf(th.TestErrorTemplate2, name, key, tc.ExpectedRegexMap[key], returned)
		}
	}
}

func matchRegexMap(regexMap map[string]string, w *httptest.ResponseRecorder) (bool, string, string, error) {
	m := make(map[string]interface{})
	err := json.Unmarshal(w.Body.Bytes(), &m)
	flatmap, _ := flatten.Flatten(m, "", flatten.RailsStyle)
	for key, element := range regexMap {
		if val, ok := flatmap[key]; ok {
			match, _ := regexp.MatchString(element, val.(string))
			if !match {
				return false, key, val.(string), err
			}
		}
	}
	return true, "", "", err
}
