package mgmt

import (
	"context"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"math/rand"
	"net/http"
	"regexp"
	"strconv"
	"testing"
	"time"

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

	"github.com/umputun/reproxy/app/discovery"
)

func TestServer_controllers(t *testing.T) {
	inf := &InformerMock{
		MappersFunc: func() []discovery.URLMapper {
			return []discovery.URLMapper{
				{
					Server: "srv1", MatchType: discovery.MTProxy,
					SrcMatch: *regexp.MustCompile("/api/(.*)"), Dst: "/blah/$1", ProviderID: discovery.PIFile,
					PingURL: "http://example.com/ping",
				},
				{
					Server: "srv2", MatchType: discovery.MTStatic,
					SrcMatch: *regexp.MustCompile("/api2/(.*)"), Dst: "/blah2/$1", ProviderID: discovery.PIDocker,
					PingURL: "http://example.com/ping2",
				},
				{
					Server: "srv2", MatchType: discovery.MTProxy,
					SrcMatch: *regexp.MustCompile("/api3/(.*)"), Dst: "/blah3/$1", ProviderID: discovery.PIDocker,
					PingURL: "http://example.com/ping3",
				},
			}
		},
	}

	port := rand.Intn(10000) + 40000
	srv := Server{Listen: fmt.Sprintf("127.0.0.1:%d", port), Informer: inf,
		AssetsWebRoot: "/static", AssetsLocation: "/www", Metrics: NewMetrics()}
	ctx, cancel := context.WithTimeout(context.Background(), 500*time.Millisecond)
	defer cancel()

	time.AfterFunc(time.Second, func() {
		cancel()
	})

	done := make(chan struct{})
	go func() {
		srv.Run(ctx)
		t.Logf("server terminated")
		done <- struct{}{}
	}()

	time.Sleep(10 * time.Millisecond)

	client := http.Client{}
	{
		req, err := http.NewRequest("GET", "http://127.0.0.1:"+strconv.Itoa(port)+"/ping", nil)
		require.NoError(t, err)
		resp, err := client.Do(req)
		require.NoError(t, err)
		defer resp.Body.Close()
		assert.Equal(t, http.StatusOK, resp.StatusCode)
		t.Logf("%+v", resp.Header)
		assert.Equal(t, "reproxy-mgmt", resp.Header.Get("App-Name"))
	}

	{
		req, err := http.NewRequest("GET", "http://127.0.0.1:"+strconv.Itoa(port)+"/routes", nil)
		require.NoError(t, err)
		resp, err := client.Do(req)
		require.NoError(t, err)
		defer resp.Body.Close()
		assert.Equal(t, http.StatusOK, resp.StatusCode)

		data := map[string][]interface{}{}
		err = json.NewDecoder(resp.Body).Decode(&data)
		require.NoError(t, err)

		assert.Equal(t, 1, len(data["srv1"]))
		assert.Equal(t, 2, len(data["srv2"]))

		assert.Contains(t, fmt.Sprintf("%v", data["srv1"][0]), `destination:/blah/$1`, data["srv1"][0])
		assert.Contains(t, fmt.Sprintf("%v", data["srv1"][0]), `route:/api/(.*)`, data["srv1"][0])
		assert.Contains(t, fmt.Sprintf("%v", data["srv1"][0]), `match:proxy`, data["srv1"][0])
		assert.Contains(t, fmt.Sprintf("%v", data["srv1"][0]), `provider:file`, data["srv1"][0])
		assert.Contains(t, fmt.Sprintf("%v", data["srv1"][0]), `ping:http://example.com/ping`, data["srv1"][0])
	}
	{
		req, err := http.NewRequest("GET", "http://127.0.0.1:"+strconv.Itoa(port)+"/metrics", nil)
		require.NoError(t, err)
		resp, err := client.Do(req)
		require.NoError(t, err)
		defer resp.Body.Close()
		assert.Equal(t, http.StatusOK, resp.StatusCode)
		body, err := ioutil.ReadAll(resp.Body)
		require.NoError(t, err)
		t.Logf("%s", string(body))

		assert.Contains(t, string(body), "promhttp_metric_handler_requests_total{code=\"200\"")
		assert.Contains(t, string(body), "promhttp_metric_handler_requests_total counter")
	}
	<-done
}
