// Copyright 2020-2023 the Pinniped contributors. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0

package federationdomainproviders

import (
	"fmt"
	"testing"

	"github.com/stretchr/testify/require"

	"go.pinniped.dev/internal/idtransform"
)

func TestFederationDomainIssuerValidations(t *testing.T) {
	tests := []struct {
		name      string
		issuer    string
		wantError string
	}{
		{
			name:      "must have an issuer",
			issuer:    "",
			wantError: "federation domain must have an issuer",
		},
		{
			name:      "returns url.Parse errors",
			issuer:    "https://example.com" + string(byte(0x7f)),
			wantError: "could not parse issuer as URL: parse \"https://example.com\\x7f\": net/url: invalid control character in URL",
		},
		{
			name:      "no hostname",
			issuer:    "https://",
			wantError: `issuer must have a hostname`,
		},
		{
			name:      "no scheme",
			issuer:    "tuna.com",
			wantError: `issuer must have "https" scheme`,
		},
		{
			name:      "bad scheme",
			issuer:    "ftp://tuna.com",
			wantError: `issuer must have "https" scheme`,
		},
		{
			name:      "fragment",
			issuer:    "https://tuna.com/fish#some-frag",
			wantError: `issuer must not have fragment`,
		},
		{
			name:      "query",
			issuer:    "https://tuna.com?some=query",
			wantError: `issuer must not have query`,
		},
		{
			name:      "username",
			issuer:    "https://username@tuna.com",
			wantError: `issuer must not have username or password`,
		},
		{
			name:      "password",
			issuer:    "https://username:password@tuna.com",
			wantError: `issuer must not have username or password`,
		},
		{
			name:   "without path",
			issuer: "https://tuna.com",
		},
		{
			name:   "with path",
			issuer: "https://tuna.com/fish/marlin",
		},
		{
			name:      "with http scheme",
			issuer:    "http://tuna.com",
			wantError: `issuer must have "https" scheme`,
		},
		{
			name:      "trailing slash in path",
			issuer:    "https://tuna.com/",
			wantError: `issuer must not have trailing slash in path`,
		},
	}
	for _, tt := range tests {
		tt := tt
		t.Run(tt.name, func(t *testing.T) {
			_, err := NewFederationDomainIssuer(tt.issuer, []*FederationDomainIdentityProvider{})
			if tt.wantError != "" {
				require.EqualError(t, err, tt.wantError)
			} else {
				require.NoError(t, err)
			}

			// This alternate constructor should perform all the same validations on the issuer string.
			_, err = NewFederationDomainIssuerWithDefaultIDP(tt.issuer, &FederationDomainIdentityProvider{
				DisplayName: "foobar",
				UID:         "foo-123",
				Transforms:  idtransform.NewTransformationPipeline(),
			})
			if tt.wantError != "" {
				require.EqualError(t, err, tt.wantError)
			} else {
				require.NoError(t, err)
			}
		})
	}
}

func TestFederationDomainGetters(t *testing.T) {
	const issuerHost = "some-issuer.com"
	const issuerPath = "/some/path"
	issuerURLString := fmt.Sprintf("https://%s%s", issuerHost, issuerPath)

	provider1 := &FederationDomainIdentityProvider{
		DisplayName: "test-name-1",
		UID:         "test-uid-1",
	}
	provider2 := &FederationDomainIdentityProvider{
		DisplayName: "test-name-2",
		UID:         "test-uid-2",
	}

	fdi, err := NewFederationDomainIssuer(issuerURLString, []*FederationDomainIdentityProvider{provider1, provider2})
	require.NoError(t, err)
	require.Equal(t, issuerURLString, fdi.Issuer())
	require.Equal(t, issuerHost, fdi.IssuerHost())
	require.Equal(t, issuerPath, fdi.IssuerPath())
	require.Equal(t, []*FederationDomainIdentityProvider{provider1, provider2}, fdi.IdentityProviders())
	require.Nil(t, fdi.DefaultIdentityProvider())

	fdi, err = NewFederationDomainIssuerWithDefaultIDP(issuerURLString, provider1)
	require.NoError(t, err)
	require.Equal(t, issuerURLString, fdi.Issuer())
	require.Equal(t, issuerHost, fdi.IssuerHost())
	require.Equal(t, issuerPath, fdi.IssuerPath())
	require.Equal(t, []*FederationDomainIdentityProvider{provider1}, fdi.IdentityProviders())
	require.Equal(t, provider1, fdi.DefaultIdentityProvider())
}