package application

import (
	"net/http"
	"net/url"
	"regexp"
	"testing"

	"github.com/stretchr/testify/assert"
	"goauthentik.io/internal/constants"
	"goauthentik.io/internal/outpost/proxyv2/types"
	api "goauthentik.io/packages/client-go"
)

func urlMustParse(u string) *url.URL {
	ur, err := url.Parse(u)
	if err != nil {
		panic(err)
	}
	return ur
}

func TestIsAllowlisted_Proxy_Single(t *testing.T) {
	a := newTestApplication()
	a.proxyConfig.Mode = api.PROXYMODE_PROXY.Ptr()

	assert.Equal(t, false, a.IsAllowlisted(urlMustParse("")))
	a.UnauthenticatedRegex = []*regexp.Regexp{
		regexp.MustCompile("^/foo"),
	}
	assert.Equal(t, true, a.IsAllowlisted(urlMustParse("http://some-host/foo")))
}

func TestIsAllowlisted_Proxy_Domain(t *testing.T) {
	a := newTestApplication()
	a.proxyConfig.Mode = api.PROXYMODE_FORWARD_DOMAIN.Ptr()

	assert.Equal(t, false, a.IsAllowlisted(urlMustParse("")))
	a.UnauthenticatedRegex = []*regexp.Regexp{
		regexp.MustCompile("^/foo"),
	}
	assert.Equal(t, false, a.IsAllowlisted(urlMustParse("http://some-host/foo")))
	a.UnauthenticatedRegex = []*regexp.Regexp{
		regexp.MustCompile("^http://some-host/foo"),
	}
	assert.Equal(t, true, a.IsAllowlisted(urlMustParse("http://some-host/foo")))
	a.UnauthenticatedRegex = []*regexp.Regexp{
		regexp.MustCompile("https://health.domain.tld/ping/*"),
	}
	assert.Equal(t, false, a.IsAllowlisted(urlMustParse("http://some-host/foo")))
	assert.Equal(t, false, a.IsAllowlisted(urlMustParse("https://health.domain.tld/")))
	assert.Equal(t, true, a.IsAllowlisted(urlMustParse("https://health.domain.tld/ping/qq")))
}

func TestAdHeaders_Standard(t *testing.T) {
	a := newTestApplication()
	h := http.Header{}
	a.addHeaders(h, &types.Claims{
		PreferredUsername: "foo",
		Groups:            []string{"foo", "bar"},
		Entitlements:      []string{"bar", "quox"},
		Email:             "bar@authentik.company",
		Name:              "foo",
		Sub:               "bar",
		RawToken:          "baz",
	})
	assert.Equal(t, http.Header{
		"X-Authentik-Email":         []string{"bar@authentik.company"},
		"X-Authentik-Entitlements":  []string{"bar|quox"},
		"X-Authentik-Groups":        []string{"foo|bar"},
		"X-Authentik-Jwt":           []string{"baz"},
		"X-Authentik-Meta-App":      []string{""},
		"X-Authentik-Meta-Jwks":     []string{""},
		"X-Authentik-Meta-Outpost":  []string{""},
		"X-Authentik-Meta-Provider": []string{a.proxyConfig.Name},
		"X-Authentik-Meta-Version":  []string{constants.UserAgentOutpost()},
		"X-Authentik-Name":          []string{"foo"},
		"X-Authentik-Uid":           []string{"bar"},
		"X-Authentik-Username":      []string{"foo"},
	}, h)
}

func TestAdHeaders_BasicAuth(t *testing.T) {
	a := newTestApplication()
	a.proxyConfig.BasicAuthEnabled = new(true)
	a.proxyConfig.BasicAuthUserAttribute = new("user")
	a.proxyConfig.BasicAuthPasswordAttribute = new("pass")
	h := http.Header{}
	a.addHeaders(h, &types.Claims{
		PreferredUsername: "foo",
		Groups:            []string{"foo", "bar"},
		Entitlements:      []string{"bar", "quox"},
		Email:             "bar@authentik.company",
		Name:              "foo",
		Sub:               "bar",
		RawToken:          "baz",
		Proxy: &types.ProxyClaims{
			UserAttributes: map[string]any{
				"user": "foo",
				"pass": "baz",
			},
		},
	})
	assert.Equal(t, http.Header{
		"Authorization":             []string{"Basic Zm9vOmJheg=="},
		"X-Authentik-Email":         []string{"bar@authentik.company"},
		"X-Authentik-Entitlements":  []string{"bar|quox"},
		"X-Authentik-Groups":        []string{"foo|bar"},
		"X-Authentik-Jwt":           []string{"baz"},
		"X-Authentik-Meta-App":      []string{""},
		"X-Authentik-Meta-Jwks":     []string{""},
		"X-Authentik-Meta-Outpost":  []string{""},
		"X-Authentik-Meta-Provider": []string{a.proxyConfig.Name},
		"X-Authentik-Meta-Version":  []string{constants.UserAgentOutpost()},
		"X-Authentik-Name":          []string{"foo"},
		"X-Authentik-Uid":           []string{"bar"},
		"X-Authentik-Username":      []string{"foo"},
	}, h)
}

func TestAdHeaders_Extra(t *testing.T) {
	a := newTestApplication()
	h := http.Header{}
	a.addHeaders(h, &types.Claims{
		PreferredUsername: "foo",
		Groups:            []string{"foo", "bar"},
		Entitlements:      []string{"bar", "quox"},
		Email:             "bar@authentik.company",
		Name:              "foo",
		Sub:               "bar",
		RawToken:          "baz",
		Proxy: &types.ProxyClaims{
			UserAttributes: map[string]any{
				"additionalHeaders": map[string]any{
					"foo": "bar",
				},
			},
		},
	})
	assert.Equal(t, http.Header{
		"Foo":                       []string{"bar"},
		"X-Authentik-Email":         []string{"bar@authentik.company"},
		"X-Authentik-Entitlements":  []string{"bar|quox"},
		"X-Authentik-Groups":        []string{"foo|bar"},
		"X-Authentik-Jwt":           []string{"baz"},
		"X-Authentik-Meta-App":      []string{""},
		"X-Authentik-Meta-Jwks":     []string{""},
		"X-Authentik-Meta-Outpost":  []string{""},
		"X-Authentik-Meta-Provider": []string{a.proxyConfig.Name},
		"X-Authentik-Meta-Version":  []string{constants.UserAgentOutpost()},
		"X-Authentik-Name":          []string{"foo"},
		"X-Authentik-Uid":           []string{"bar"},
		"X-Authentik-Username":      []string{"foo"},
	}, h)
}

func TestAdHeaders_UnderscoreInitial(t *testing.T) {
	a := newTestApplication()
	h := http.Header{}
	h.Set("X_AUTHENTIK_USERNAME", "another user")
	h.Set("X-Authentik_username", "another user")
	a.addHeaders(h, &types.Claims{
		PreferredUsername: "foo",
		Groups:            []string{"foo", "bar"},
		Entitlements:      []string{"bar", "quox"},
		Email:             "bar@authentik.company",
		Name:              "foo",
		Sub:               "bar",
		RawToken:          "baz",
	})
	assert.Equal(t, http.Header{
		"X-Authentik-Email":         []string{"bar@authentik.company"},
		"X-Authentik-Entitlements":  []string{"bar|quox"},
		"X-Authentik-Groups":        []string{"foo|bar"},
		"X-Authentik-Jwt":           []string{"baz"},
		"X-Authentik-Meta-App":      []string{""},
		"X-Authentik-Meta-Jwks":     []string{""},
		"X-Authentik-Meta-Outpost":  []string{""},
		"X-Authentik-Meta-Provider": []string{a.proxyConfig.Name},
		"X-Authentik-Meta-Version":  []string{constants.UserAgentOutpost()},
		"X-Authentik-Name":          []string{"foo"},
		"X-Authentik-Uid":           []string{"bar"},
		"X-Authentik-Username":      []string{"foo"},
	}, h)
}
