package validation

import (
	"encoding/base64"

	"github.com/oauth2-proxy/oauth2-proxy/v7/pkg/apis/options"
	. "github.com/onsi/ginkgo"
	. "github.com/onsi/ginkgo/extensions/table"
	. "github.com/onsi/gomega"
)

var _ = Describe("Headers", func() {
	type validateHeaderTableInput struct {
		headers      []options.Header
		expectedMsgs []string
	}

	validHeader1 := options.Header{
		Name: "X-Email",
		Values: []options.HeaderValue{
			{
				ClaimSource: &options.ClaimSource{
					Claim: "email",
				},
			},
		},
	}

	validHeader2 := options.Header{
		Name: "X-Forwarded-Auth",
		Values: []options.HeaderValue{
			{
				SecretSource: &options.SecretSource{
					Value: []byte(base64.StdEncoding.EncodeToString([]byte("secret"))),
				},
			},
		},
	}

	validHeader3 := options.Header{
		Name: "Authorization",
		Values: []options.HeaderValue{
			{
				ClaimSource: &options.ClaimSource{
					Claim: "email",
					BasicAuthPassword: &options.SecretSource{
						Value: []byte(base64.StdEncoding.EncodeToString([]byte("secret"))),
					},
				},
			},
		},
	}

	DescribeTable("validateHeaders",
		func(in validateHeaderTableInput) {
			Expect(validateHeaders(in.headers)).To(ConsistOf(in.expectedMsgs))
		},
		Entry("with no headers", validateHeaderTableInput{
			headers:      []options.Header{},
			expectedMsgs: []string{},
		}),
		Entry("with valid headers", validateHeaderTableInput{
			headers: []options.Header{
				validHeader1,
				validHeader2,
				validHeader3,
			},
			expectedMsgs: []string{},
		}),
		Entry("with multiple headers with the same name", validateHeaderTableInput{
			headers: []options.Header{
				validHeader1,
				validHeader1,
				validHeader2,
				validHeader2,
			},
			expectedMsgs: []string{
				"multiple headers found with name \"X-Email\": header names must be unique",
				"multiple headers found with name \"X-Forwarded-Auth\": header names must be unique",
			},
		}),
		Entry("with an unamed header", validateHeaderTableInput{
			headers: []options.Header{
				{},
				validHeader2,
			},
			expectedMsgs: []string{
				"header has empty name: names are required for all headers",
			},
		}),
		Entry("with a header which has a claim and secret source", validateHeaderTableInput{
			headers: []options.Header{
				{
					Name: "With-Claim-And-Secret",
					Values: []options.HeaderValue{
						{
							ClaimSource:  &options.ClaimSource{},
							SecretSource: &options.SecretSource{},
						},
					},
				},
				validHeader1,
			},
			expectedMsgs: []string{
				"invalid header \"With-Claim-And-Secret\": invalid values: header value has multiple entries: only one entry per value is allowed",
			},
		}),
		Entry("with a header which has a claim without a claim", validateHeaderTableInput{
			headers: []options.Header{
				{
					Name: "Without-Claim",
					Values: []options.HeaderValue{
						{
							ClaimSource: &options.ClaimSource{
								Prefix: "prefix",
							},
						},
					},
				},
				validHeader3,
			},
			expectedMsgs: []string{
				"invalid header \"Without-Claim\": invalid values: claim should not be empty",
			},
		}),
		Entry("with a header with invalid secret source", validateHeaderTableInput{
			headers: []options.Header{
				{
					Name: "With-Invalid-Secret",
					Values: []options.HeaderValue{
						{
							SecretSource: &options.SecretSource{},
						},
					},
				},
				validHeader1,
			},
			expectedMsgs: []string{
				"invalid header \"With-Invalid-Secret\": invalid values: multiple values specified for secret source: specify either value, fromEnv of fromFile",
			},
		}),
		Entry("with a header with invalid basicAuthPassword source", validateHeaderTableInput{
			headers: []options.Header{
				{
					Name: "With-Invalid-Basic-Auth",
					Values: []options.HeaderValue{
						{
							ClaimSource: &options.ClaimSource{
								Claim: "user",
								BasicAuthPassword: &options.SecretSource{
									FromEnv: "UNKNOWN_ENV",
								},
							},
						},
					},
				},
				validHeader1,
			},
			expectedMsgs: []string{
				"invalid header \"With-Invalid-Basic-Auth\": invalid values: invalid basicAuthPassword: error loading secret from environent: no value for for key \"UNKNOWN_ENV\"",
			},
		}),
	)
})