package router

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

	"github.com/owncloud/ocis/v2/ocis-pkg/log"
	"github.com/owncloud/ocis/v2/services/proxy/pkg/config"
	"github.com/owncloud/ocis/v2/services/proxy/pkg/config/defaults"
)

type matchertest struct {
	method, endpoint, target string
	unprotected              bool
	matches                  bool
}

func TestPrefixRouteMatcher(t *testing.T) {
	cfg := defaults.DefaultConfig()
	cfg.Policies = defaults.DefaultPolicies()

	table := []matchertest{
		{endpoint: "/foobar", target: "/foobar/baz/some/url", matches: true},
		{endpoint: "/fobar", target: "/foobar/baz/some/url", matches: false},
	}

	for _, test := range table {
		u, _ := url.Parse(test.target)
		matched := prefixRouteMatcher(test.endpoint, *u)
		if matched != test.matches {
			t.Errorf("PrefixRouteMatcher returned %t expected %t for endpoint: %s and target %s",
				matched, test.matches, test.endpoint, u.String())
		}
	}
}

func TestQueryRouteMatcher(t *testing.T) {
	cfg := defaults.DefaultConfig()
	cfg.Policies = defaults.DefaultPolicies()

	table := []matchertest{
		{endpoint: "/foobar?parameter=true", target: "/foobar/baz/some/url?parameter=true", matches: true},
		{endpoint: "/foobar", target: "/foobar/baz/some/url?parameter=true", matches: false},
		{endpoint: "/foobar?parameter=false", target: "/foobar/baz/some/url?parameter=true", matches: false},
		{endpoint: "/foobar?parameter=false&other=true", target: "/foobar/baz/some/url?parameter=true", matches: false},
		{
			endpoint: "/foobar?parameter=false&other=true",
			target:   "/foobar/baz/some/url?parameter=false&other=true",
			matches:  true,
		},
		{endpoint: "/fobar", target: "/foobar", matches: false},
	}

	for _, test := range table {
		u, _ := url.Parse(test.target)
		matched := queryRouteMatcher(test.endpoint, *u)
		if matched != test.matches {
			t.Errorf("QueryRouteMatcher returned %t expected %t for endpoint: %s and target %s",
				matched, test.matches, test.endpoint, u.String())
		}
	}
}

func TestRegexRouteMatcher(t *testing.T) {
	cfg := defaults.DefaultConfig()
	cfg.Policies = defaults.DefaultPolicies()
	rt := New(cfg.PolicySelector, cfg.Policies, log.NewLogger())

	table := []matchertest{
		{endpoint: ".*some\\/url.*parameter=true", target: "/foobar/baz/some/url?parameter=true", matches: true},
		{endpoint: "([\\])\\w+", target: "/foobar/baz/some/url?parameter=true", matches: false},
	}

	for _, test := range table {
		u, _ := url.Parse(test.target)
		matched := rt.regexRouteMatcher(test.endpoint, *u)
		if matched != test.matches {
			t.Errorf("RegexRouteMatcher returned %t expected %t for endpoint: %s and target %s",
				matched, test.matches, test.endpoint, u.String())
		}
	}
}

func TestSingleJoiningSlash(t *testing.T) {
	type test struct {
		a, b, result string
	}

	table := []test{
		{a: "a", b: "b", result: "a/b"},
		{a: "a/", b: "b", result: "a/b"},
		{a: "a", b: "/b", result: "a/b"},
		{a: "a/", b: "/b", result: "a/b"},
	}

	for _, test := range table {
		p := singleJoiningSlash(test.a, test.b)
		if p != test.result {
			t.Errorf("SingleJoiningSlash got %s expected %s", p, test.result)
		}
	}
}

func TestRouter(t *testing.T) {
	svr := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		fmt.Fprintf(w, "ok")
	}))
	defer svr.Close()

	selector := &config.PolicySelector{
		Static: &config.StaticSelectorConf{
			Policy: "default",
		},
	}

	policies := []config.Policy{
		{
			Name: "default",
			Routes: []config.Route{
				{Type: config.PrefixRoute, Endpoint: "/web/unprotected/demo/", Backend: "http://web", Unprotected: true},
				{Type: config.PrefixRoute, Endpoint: "/dav", Backend: "http://ocdav"},
				{Type: config.PrefixRoute, Method: "REPORT", Endpoint: "/dav", Backend: "http://ocis-webdav"},
			},
		},
	}

	router := New(selector, policies, log.NewLogger())

	table := []matchertest{
		{method: "PROPFIND", endpoint: "/dav/files/demo/", target: "ocdav"},
		{method: "REPORT", endpoint: "/dav/files/demo/", target: "ocis-webdav"},
		{method: "GET", endpoint: "/web/unprotected/demo/", target: "web", unprotected: true},
	}

	for _, test := range table {
		r := httptest.NewRequest(test.method, test.endpoint, nil)
		routingInfo, ok := router.Route(r)
		if !ok {
			t.Errorf("TestRouter router.Route failed to route the request.")
		}

		if routingInfo.IsRouteUnprotected() != test.unprotected {
			t.Errorf("TestRouter route flag unprotected expected to be %t got %t", test.unprotected, routingInfo.IsRouteUnprotected())
		}

		routingInfo.Director()(r)

		if r.URL.Host != test.target {
			t.Errorf("TestRouter got host %s expected %s", r.URL.Host, test.target)
		}
	}
}
