package api

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io"
	"net/http"
	"net/http/httptest"
	"net/url"
	"testing"
	"time"

	jwt "github.com/golang-jwt/jwt/v5"
	"github.com/stretchr/testify/require"
	"github.com/stretchr/testify/suite"
	"github.com/supabase/auth/internal/conf"
	"github.com/supabase/auth/internal/models"
)

const dateInPast = "2001-02-03T04:05:06.789"
const dateInFarFuture = "2999-02-03T04:05:06.789"
const oneHour = "PT1H"

type SSOTestSuite struct {
	suite.Suite
	API      *API
	Config   *conf.GlobalConfiguration
	AdminJWT string
}

func TestSSO(t *testing.T) {
	api, config, err := setupAPIForTest()
	require.NoError(t, err)

	ts := &SSOTestSuite{
		API:    api,
		Config: config,
	}
	defer api.db.Close()

	if config.SAML.Enabled {
		suite.Run(t, ts)
	}
}

func (ts *SSOTestSuite) SetupTest() {
	models.TruncateAll(ts.API.db)

	claims := &AccessTokenClaims{
		Role: "supabase_admin",
	}
	token, err := jwt.NewWithClaims(jwt.SigningMethodHS256, claims).SignedString([]byte(ts.Config.JWT.Secret))
	require.NoError(ts.T(), err, "Error generating admin jwt")

	ts.AdminJWT = token
}

func (ts *SSOTestSuite) TestNonAdminJWT() {
	// TODO
}

func (ts *SSOTestSuite) TestAdminListEmptySSOProviders() {
	req := httptest.NewRequest(http.MethodGet, "http://localhost/admin/sso/providers", nil)
	req.Header.Set("Authorization", "Bearer "+ts.AdminJWT)
	w := httptest.NewRecorder()

	ts.API.handler.ServeHTTP(w, req)

	require.Equal(ts.T(), http.StatusOK, w.Code)

	body, err := io.ReadAll(w.Body)
	require.NoError(ts.T(), err)

	var result struct {
		Items     []interface{} `json:"items"`
		NextToken string        `json:"next_token"`
	}

	require.NoError(ts.T(), json.Unmarshal(body, &result))
	require.Equal(ts.T(), len(result.Items), 0)
	require.Equal(ts.T(), result.NextToken, "")
}

func (ts *SSOTestSuite) TestAdminGetSSOProviderNotExist() {
	examples := []struct {
		URL string
	}{
		{
			URL: "http://localhost/admin/sso/providers/not-a-uuid",
		},
		{
			URL: "http://localhost/admin/sso/providers/677477db-3f51-4038-bc05-c6bb9bdc3c32",
		},
	}

	for _, example := range examples {
		req := httptest.NewRequest(http.MethodGet, example.URL, nil)
		req.Header.Set("Authorization", "Bearer "+ts.AdminJWT)
		w := httptest.NewRecorder()

		ts.API.handler.ServeHTTP(w, req)

		require.Equal(ts.T(), http.StatusNotFound, w.Code)
	}
}

func configurableSAMLIDPMetadata(entityID, validUntil, cacheDuration string) string {
	return fmt.Sprintf(`<?xml version="1.0" encoding="UTF-8"?><md:EntityDescriptor xmlns:md="urn:oasis:names:tc:SAML:2.0:metadata" entityID="%s" validUntil='%s' cacheDuration='%s'>
  <md:IDPSSODescriptor WantAuthnRequestsSigned="false" protocolSupportEnumeration="urn:oasis:names:tc:SAML:2.0:protocol">
    <md:KeyDescriptor use="signing">
      <ds:KeyInfo xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
        <ds:X509Data>
          <ds:X509Certificate>MIIDdDCCAlygAwIBAgIGAYKSjRZiMA0GCSqGSIb3DQEBCwUAMHsxFDASBgNVBAoTC0dvb2dsZSBJ
bmMuMRYwFAYDVQQHEw1Nb3VudGFpbiBWaWV3MQ8wDQYDVQQDEwZHb29nbGUxGDAWBgNVBAsTD0dv
b2dsZSBGb3IgV29yazELMAkGA1UEBhMCVVMxEzARBgNVBAgTCkNhbGlmb3JuaWEwHhcNMjIwODEy
MTQ1NDU1WhcNMjcwODExMTQ1NDU1WjB7MRQwEgYDVQQKEwtHb29nbGUgSW5jLjEWMBQGA1UEBxMN
TW91bnRhaW4gVmlldzEPMA0GA1UEAxMGR29vZ2xlMRgwFgYDVQQLEw9Hb29nbGUgRm9yIFdvcmsx
CzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpDYWxpZm9ybmlhMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A
MIIBCgKCAQEAlncFzErcnZm7ZWO71NZStnCIAoYNKf6Uw3LPLzcvk0YrA/eBC3PVDHSfahi+apGO
Ytdq7IQUvBdto3rJTvP49fjyO0WLbAbiPC+dILt2Gx9kttxpSp99Bf+8ObL/fTy5Y2oHbJBfBX1V
qfDQIY0fcej3AndFYUOE0gZXyeSbnROB8W1PzHxOc7rq1mlas0rvyja7AK4gwXjIwyIGsFDmHnve
buqWOYMzOT9oD+iQq9BWYVHkXGZn0BXzKtnw9w8I3IxQdndUoCl95pYRIvdl1b0dWdO9cXtSsTkL
kAa8B/mCQcF4W2M3t/yKtrcLcRTALg3/Hc+Xz+3BpY/fSDk1SwIDAQABMA0GCSqGSIb3DQEBCwUA
A4IBAQCER02WLf6bKwTGVD/3VTntetIiETuPs46Dum8blbsg+2BYdAHIQcB9cLuMRosIw0nYj54m
SfiyfoWGcx3CkMup1MtKyWu+SqDHl9Bpf+GFLG0ngKD/zB6xwpv/TCi+g/FBYe2TvzD6B1V0z7Vs
Xf+Gc2TWBKmCuKf/g2AUt7IQLpOaqxuJVoZjp4sEMov6d3FnaoHQEd0lg+XmnYfLNtwe3QRSU0BD
x6lVV4kXi0x0n198/gkjnA85rPZoZ6dmqHtkcM0Gabgg6KEE5ubSDlWDsdv27uANceCZAoxd1+in
4/KqqkhynnbJs7Op5ZX8cckiHGGTGHNb35kys/XukuCo</ds:X509Certificate>
        </ds:X509Data>
      </ds:KeyInfo>
    </md:KeyDescriptor>
    <md:NameIDFormat>urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress</md:NameIDFormat>
    <md:SingleSignOnService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect" Location="%s"/>
    <md:SingleSignOnService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" Location="%s"/>
  </md:IDPSSODescriptor>
</md:EntityDescriptor>`, entityID, validUntil, cacheDuration, entityID, entityID)

}

func (ts *SSOTestSuite) TestIsStaleSAMLMetadata() {

	// https://en.wikipedia.org/wiki/ISO_8601
	currentTime := time.Now()
	currentTimeAsISO8601 := currentTime.UTC().Format("2006-01-02T15:04:05Z07:00")
	examples := []struct {
		Description           string
		Metadata              []byte
		IsStale               bool
		CacheDurationExceeded bool
	}{
		{
			Description:           "Metadata is valid and within cache duration",
			Metadata:              []byte(configurableSAMLIDPMetadata("https://accounts.google.com/o/saml2?idpid=EXAMPLE-B", dateInFarFuture, oneHour)),
			IsStale:               false,
			CacheDurationExceeded: false,
		},
		{

			Description:           "Metadata is valid but is a minute past cache duration",
			Metadata:              []byte(configurableSAMLIDPMetadata("https://accounts.google.com/o/saml2?idpid=EXAMPLE-B", currentTimeAsISO8601, oneHour)),
			IsStale:               true,
			CacheDurationExceeded: true,
		},

		{
			Description:           "Metadata is invalid but within cache duration",
			Metadata:              []byte(configurableSAMLIDPMetadata("https://accounts.google.com/o/saml2?idpid=EXAMPLE-B", dateInPast, oneHour)),
			IsStale:               true,
			CacheDurationExceeded: false,
		},
	}

	for _, example := range examples {
		metadata, err := parseSAMLMetadata(example.Metadata)
		require.NoError(ts.T(), err)
		provider := models.SAMLProvider{
			EntityID:    metadata.EntityID,
			MetadataXML: string(example.Metadata),
			UpdatedAt:   currentTime,
		}
		if example.CacheDurationExceeded {
			provider.UpdatedAt = currentTime.Add(-time.Minute * 59)
		}

		require.Equal(ts.T(), example.IsStale, IsSAMLMetadataStale(metadata, provider))
	}

}

func validSAMLIDPMetadata(entityID string) string {
	return fmt.Sprintf(`<?xml version="1.0" encoding="UTF-8"?><md:EntityDescriptor xmlns:md="urn:oasis:names:tc:SAML:2.0:metadata" entityID="%s">
  <md:IDPSSODescriptor WantAuthnRequestsSigned="false" protocolSupportEnumeration="urn:oasis:names:tc:SAML:2.0:protocol">
    <md:KeyDescriptor use="signing">
      <ds:KeyInfo xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
        <ds:X509Data>
          <ds:X509Certificate>MIIDdDCCAlygAwIBAgIGAYKSjRZiMA0GCSqGSIb3DQEBCwUAMHsxFDASBgNVBAoTC0dvb2dsZSBJ
bmMuMRYwFAYDVQQHEw1Nb3VudGFpbiBWaWV3MQ8wDQYDVQQDEwZHb29nbGUxGDAWBgNVBAsTD0dv
b2dsZSBGb3IgV29yazELMAkGA1UEBhMCVVMxEzARBgNVBAgTCkNhbGlmb3JuaWEwHhcNMjIwODEy
MTQ1NDU1WhcNMjcwODExMTQ1NDU1WjB7MRQwEgYDVQQKEwtHb29nbGUgSW5jLjEWMBQGA1UEBxMN
TW91bnRhaW4gVmlldzEPMA0GA1UEAxMGR29vZ2xlMRgwFgYDVQQLEw9Hb29nbGUgRm9yIFdvcmsx
CzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpDYWxpZm9ybmlhMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A
MIIBCgKCAQEAlncFzErcnZm7ZWO71NZStnCIAoYNKf6Uw3LPLzcvk0YrA/eBC3PVDHSfahi+apGO
Ytdq7IQUvBdto3rJTvP49fjyO0WLbAbiPC+dILt2Gx9kttxpSp99Bf+8ObL/fTy5Y2oHbJBfBX1V
qfDQIY0fcej3AndFYUOE0gZXyeSbnROB8W1PzHxOc7rq1mlas0rvyja7AK4gwXjIwyIGsFDmHnve
buqWOYMzOT9oD+iQq9BWYVHkXGZn0BXzKtnw9w8I3IxQdndUoCl95pYRIvdl1b0dWdO9cXtSsTkL
kAa8B/mCQcF4W2M3t/yKtrcLcRTALg3/Hc+Xz+3BpY/fSDk1SwIDAQABMA0GCSqGSIb3DQEBCwUA
A4IBAQCER02WLf6bKwTGVD/3VTntetIiETuPs46Dum8blbsg+2BYdAHIQcB9cLuMRosIw0nYj54m
SfiyfoWGcx3CkMup1MtKyWu+SqDHl9Bpf+GFLG0ngKD/zB6xwpv/TCi+g/FBYe2TvzD6B1V0z7Vs
Xf+Gc2TWBKmCuKf/g2AUt7IQLpOaqxuJVoZjp4sEMov6d3FnaoHQEd0lg+XmnYfLNtwe3QRSU0BD
x6lVV4kXi0x0n198/gkjnA85rPZoZ6dmqHtkcM0Gabgg6KEE5ubSDlWDsdv27uANceCZAoxd1+in
4/KqqkhynnbJs7Op5ZX8cckiHGGTGHNb35kys/XukuCo</ds:X509Certificate>
        </ds:X509Data>
      </ds:KeyInfo>
    </md:KeyDescriptor>
    <md:NameIDFormat>urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress</md:NameIDFormat>
    <md:SingleSignOnService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect" Location="%s"/>
    <md:SingleSignOnService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" Location="%s"/>
  </md:IDPSSODescriptor>
</md:EntityDescriptor>`, entityID, entityID, entityID)
}

func (ts *SSOTestSuite) TestAdminCreateSSOProvider() {
	examples := []struct {
		StatusCode int
		Request    map[string]interface{}
	}{
		{
			StatusCode: http.StatusBadRequest,
			Request:    map[string]interface{}{},
		},
		{
			StatusCode: http.StatusBadRequest,
			Request: map[string]interface{}{
				"type": "saml",
			},
		},
		{
			StatusCode: http.StatusBadRequest,
			Request: map[string]interface{}{
				"type": "oidc",
			},
		},
		{
			StatusCode: http.StatusCreated,
			Request: map[string]interface{}{
				"type":         "saml",
				"metadata_xml": validSAMLIDPMetadata("https://accounts.google.com/o/saml2?idpid=EXAMPLE-A"),
			},
		},
		{
			StatusCode: http.StatusCreated,
			Request: map[string]interface{}{
				"type":         "saml",
				"metadata_xml": validSAMLIDPMetadata("https://accounts.google.com/o/saml2?idpid=EXAMPLE-B"),
			},
		},
		{
			StatusCode: http.StatusCreated,
			Request: map[string]interface{}{
				"type":         "saml",
				"metadata_xml": validSAMLIDPMetadata("https://accounts.google.com/o/saml2?idpid=EXAMPLE-DUPLICATE"),
			},
		},
		{
			StatusCode: http.StatusCreated,
			Request: map[string]interface{}{
				"type":         "saml",
				"metadata_xml": validSAMLIDPMetadata("https://accounts.google.com/o/saml2?idpid=EXAMPLE-WITH-ATTRIBUTE-MAPPING"),
				"attribute_mapping": map[string]interface{}{
					"keys": map[string]interface{}{
						"username": map[string]interface{}{
							"name": "mail",
						},
					},
				},
			},
		},
		{
			StatusCode: http.StatusUnprocessableEntity,
			Request: map[string]interface{}{
				"type":         "saml",
				"metadata_xml": validSAMLIDPMetadata("https://accounts.google.com/o/saml2?idpid=EXAMPLE-DUPLICATE"),
			},
		},
		{
			StatusCode: http.StatusCreated,
			Request: map[string]interface{}{
				"type":         "saml",
				"metadata_xml": validSAMLIDPMetadata("https://accounts.google.com/o/saml2?idpid=EXAMPLE-WITH-DOMAIN-A"),
				"domains": []string{
					"example.com",
				},
			},
		},
		{
			StatusCode: http.StatusBadRequest,
			Request: map[string]interface{}{
				"type":         "saml",
				"metadata_xml": validSAMLIDPMetadata("https://accounts.google.com/o/saml2?idpid=EXAMPLE-WITH-DOMAIN-B"),
				"domains": []string{
					"example.com",
				},
			},
		},
		{
			StatusCode: http.StatusBadRequest,
			Request: map[string]interface{}{
				"type":         "saml",
				"metadata_url": "https://accounts.google.com/o/saml2?idpid=EXAMPLE-WITH-METADATA-URL-TOO",
				"metadata_xml": validSAMLIDPMetadata("https://accounts.google.com/o/saml2?idpid=EXAMPLE-WITH-METADATA-URL-TOO"),
			},
		},
		{
			StatusCode: http.StatusBadRequest,
			Request: map[string]interface{}{
				"type":         "saml",
				"metadata_url": "http://accounts.google.com/o/saml2?idpid=EXAMPLE-WITH-METADATA-OVER-HTTP",
			},
		},
		{
			StatusCode: http.StatusBadRequest,
			Request: map[string]interface{}{
				"type":         "saml",
				"metadata_url": "https://accounts.google.com\\o/saml2?idpid=EXAMPLE-WITH-INVALID-METADATA-URL",
			},
		},
		// TODO: add example with metadata_url
	}

	for i, example := range examples {
		body, err := json.Marshal(example.Request)
		require.NoError(ts.T(), err)

		req := httptest.NewRequest(http.MethodPost, "http://localhost/admin/sso/providers", bytes.NewBuffer(body))
		req.Header.Set("Authorization", "Bearer "+ts.AdminJWT)
		w := httptest.NewRecorder()

		ts.API.handler.ServeHTTP(w, req)

		response, err := io.ReadAll(w.Body)
		require.NoError(ts.T(), err)

		require.Equal(ts.T(), example.StatusCode, w.Code, "Example %d failed with body %q", i, response)

		if example.StatusCode != http.StatusCreated {
			continue
		}

		// now check if the provider can be queried (GET)
		var provider struct {
			ID string `json:"id"`
		}

		require.NoError(ts.T(), json.Unmarshal(response, &provider))

		req = httptest.NewRequest(http.MethodGet, "http://localhost/admin/sso/providers/"+provider.ID, nil)
		req.Header.Set("Authorization", "Bearer "+ts.AdminJWT)
		w = httptest.NewRecorder()

		ts.API.handler.ServeHTTP(w, req)

		response, err = io.ReadAll(w.Body)
		require.NoError(ts.T(), err)

		require.Equal(ts.T(), http.StatusOK, w.Code)

		originalProviderID := provider.ID
		provider.ID = ""

		require.NoError(ts.T(), json.Unmarshal(response, &provider))
		require.Equal(ts.T(), provider.ID, originalProviderID)

		// now check if the provider can be queried (List)
		var providers struct {
			Items []struct {
				ID string `json:"id"`
			} `json:"items"`
		}

		req = httptest.NewRequest(http.MethodGet, "http://localhost/admin/sso/providers", nil)
		req.Header.Set("Authorization", "Bearer "+ts.AdminJWT)
		w = httptest.NewRecorder()

		ts.API.handler.ServeHTTP(w, req)

		response, err = io.ReadAll(w.Body)
		require.NoError(ts.T(), err)

		require.NoError(ts.T(), json.Unmarshal(response, &providers))

		contained := false
		for _, listProvider := range providers.Items {
			if listProvider.ID == provider.ID {
				contained = true
				break
			}
		}

		require.True(ts.T(), contained)
	}
}

func (ts *SSOTestSuite) TestAdminUpdateSSOProvider() {
	providers := []struct {
		ID      string
		Request map[string]interface{}
	}{
		{
			Request: map[string]interface{}{
				"type":         "saml",
				"metadata_xml": validSAMLIDPMetadata("https://accounts.google.com/o/saml2?idpid=EXAMPLE-A"),
			},
		},
		{
			Request: map[string]interface{}{
				"type":         "saml",
				"metadata_xml": validSAMLIDPMetadata("https://accounts.google.com/o/saml2?idpid=EXAMPLE-C"),
				"domains": []string{
					"example.com",
				},
			},
		},
	}

	for i, example := range providers {
		body, err := json.Marshal(example.Request)
		require.NoError(ts.T(), err)

		req := httptest.NewRequest(http.MethodPost, "http://localhost/admin/sso/providers", bytes.NewBuffer(body))
		req.Header.Set("Authorization", "Bearer "+ts.AdminJWT)
		w := httptest.NewRecorder()

		ts.API.handler.ServeHTTP(w, req)

		response, err := io.ReadAll(w.Body)
		require.NoError(ts.T(), err)

		var payload struct {
			ID string `json:"id"`
		}

		require.NoError(ts.T(), json.Unmarshal(response, &payload))

		providers[i].ID = payload.ID
	}

	examples := []struct {
		ID      string
		Status  int
		Request map[string]interface{}
	}{
		{
			ID:     providers[0].ID,
			Status: http.StatusBadRequest, // changing entity ID
			Request: map[string]interface{}{
				"metadata_xml": validSAMLIDPMetadata("https://accounts.google.com/o/saml2?idpid=EXAMPLE-B"),
			},
		},
		{
			ID:     providers[0].ID,
			Status: http.StatusBadRequest, // domain already exists
			Request: map[string]interface{}{
				"domains": []string{
					"example.com",
				},
			},
		},
		{
			ID:     providers[1].ID,
			Status: http.StatusOK,
			Request: map[string]interface{}{
				"domains": []string{
					"example.com",
					"example.org",
				},
			},
		},
		{
			ID:     providers[1].ID,
			Status: http.StatusOK,
			Request: map[string]interface{}{
				"attribute_mapping": map[string]interface{}{
					"keys": map[string]interface{}{
						"username": map[string]interface{}{
							"name": "mail",
						},
					},
				},
			},
		},
	}

	for _, example := range examples {
		body, err := json.Marshal(example.Request)
		require.NoError(ts.T(), err)

		req := httptest.NewRequest(http.MethodPut, "http://localhost/admin/sso/providers/"+example.ID, bytes.NewBuffer(body))
		req.Header.Set("Authorization", "Bearer "+ts.AdminJWT)
		w := httptest.NewRecorder()

		ts.API.handler.ServeHTTP(w, req)

		require.Equal(ts.T(), w.Code, example.Status)
	}
}

func (ts *SSOTestSuite) TestAdminDeleteSSOProvider() {
	providers := []struct {
		ID      string
		Request map[string]interface{}
	}{
		{
			Request: map[string]interface{}{
				"type":         "saml",
				"metadata_xml": validSAMLIDPMetadata("https://accounts.google.com/o/saml2?idpid=EXAMPLE-A"),
			},
		},
	}

	for i, example := range providers {
		body, err := json.Marshal(example.Request)
		require.NoError(ts.T(), err)

		req := httptest.NewRequest(http.MethodPost, "http://localhost/admin/sso/providers", bytes.NewBuffer(body))
		req.Header.Set("Authorization", "Bearer "+ts.AdminJWT)
		w := httptest.NewRecorder()

		ts.API.handler.ServeHTTP(w, req)

		response, err := io.ReadAll(w.Body)
		require.NoError(ts.T(), err)

		var payload struct {
			ID string `json:"id"`
		}

		require.NoError(ts.T(), json.Unmarshal(response, &payload))

		providers[i].ID = payload.ID
	}

	examples := []struct {
		ID     string
		Status int
	}{
		{
			ID:     providers[0].ID,
			Status: http.StatusOK,
		},
	}

	for _, example := range examples {
		req := httptest.NewRequest(http.MethodDelete, "http://localhost/admin/sso/providers/"+example.ID, nil)
		req.Header.Set("Authorization", "Bearer "+ts.AdminJWT)
		w := httptest.NewRecorder()

		ts.API.handler.ServeHTTP(w, req)

		require.Equal(ts.T(), w.Code, example.Status)
	}

	check := []struct {
		ID string
	}{
		{
			ID: providers[0].ID,
		},
	}

	for _, example := range check {
		req := httptest.NewRequest(http.MethodGet, "http://localhost/admin/sso/providers/"+example.ID, nil)
		req.Header.Set("Authorization", "Bearer "+ts.AdminJWT)
		w := httptest.NewRecorder()

		ts.API.handler.ServeHTTP(w, req)

		require.Equal(ts.T(), http.StatusNotFound, w.Code)
	}
}

func (ts *SSOTestSuite) TestSingleSignOn() {
	providers := []struct {
		ID      string
		Request map[string]interface{}
	}{
		{
			// creates a SAML provider (EXAMPLE-A)
			// does not have a domain mapping
			Request: map[string]interface{}{
				"type":         "saml",
				"metadata_xml": validSAMLIDPMetadata("https://accounts.google.com/o/saml2?idpid=EXAMPLE-A"),
			},
		},
		{
			// creates a SAML provider (EXAMPLE-B)
			// does have a domain mapping on example.com
			Request: map[string]interface{}{
				"type": "saml",
				"domains": []string{
					"example.com",
				},
				"metadata_xml": validSAMLIDPMetadata("https://accounts.google.com/o/saml2?idpid=EXAMPLE-B"),
			},
		},
	}

	for i, example := range providers {
		body, err := json.Marshal(example.Request)
		require.NoError(ts.T(), err)

		req := httptest.NewRequest(http.MethodPost, "http://localhost/admin/sso/providers", bytes.NewBuffer(body))
		req.Header.Set("Authorization", "Bearer "+ts.AdminJWT)
		w := httptest.NewRecorder()

		ts.API.handler.ServeHTTP(w, req)

		response, err := io.ReadAll(w.Body)
		require.NoError(ts.T(), err)

		var payload struct {
			ID string `json:"id"`
		}

		require.NoError(ts.T(), json.Unmarshal(response, &payload))

		providers[i].ID = payload.ID
	}

	examples := []struct {
		Code    int
		Request map[string]interface{}
		URL     string
	}{
		{
			// call /sso with provider_id (EXAMPLE-A)
			// should be successful and redirect to the EXAMPLE-A SSO URL
			Request: map[string]interface{}{
				"provider_id": providers[0].ID,
			},
			Code: http.StatusSeeOther,
			URL:  "https://accounts.google.com/o/saml2?idpid=EXAMPLE-A",
		},
		{
			// call /sso with provider_id (EXAMPLE-A) and SSO PKCE
			// should be successful and redirect to the EXAMPLE-A SSO URL
			Request: map[string]interface{}{
				"provider_id":           providers[0].ID,
				"code_challenge":        "vby3iMQ4XUuycKkEyNsYHXshPql1Dod7Ebey2iXTXm4",
				"code_challenge_method": "s256",
			},
			Code: http.StatusSeeOther,
			URL:  "https://accounts.google.com/o/saml2?idpid=EXAMPLE-A",
		},
		{
			// call /sso with domain=example.com (provider=EXAMPLE-B)
			// should be successful and redirect to the EXAMPLE-B SSO URL
			Request: map[string]interface{}{
				"domain": "example.com",
			},
			Code: http.StatusSeeOther,
			URL:  "https://accounts.google.com/o/saml2?idpid=EXAMPLE-B",
		},
		{
			// call /sso with domain=example.com (provider=EXAMPLE-B)
			// should be successful and redirect to the EXAMPLE-B SSO URL
			Request: map[string]interface{}{
				"domain":             "example.com",
				"skip_http_redirect": true,
			},
			Code: http.StatusOK,
			URL:  "https://accounts.google.com/o/saml2?idpid=EXAMPLE-B",
		},
		{
			// call /sso with domain=example.org (no such provider)
			// should be unsuccessful with 404
			Request: map[string]interface{}{
				"domain": "example.org",
			},
			Code: http.StatusNotFound,
		},
		{
			// call /sso with a provider_id=<random-uuid> (no such provider)
			// should be unsuccessful with 404
			Request: map[string]interface{}{
				"provider_id": "14d906bf-9bd5-4734-b7d1-3904e240610e",
			},
			Code: http.StatusNotFound,
		},
	}

	for _, example := range examples {
		body, err := json.Marshal(example.Request)
		require.NoError(ts.T(), err)

		req := httptest.NewRequest(http.MethodPost, "http://localhost/sso", bytes.NewBuffer(body))
		// no authorization header intentional, this is a login endpoint
		w := httptest.NewRecorder()

		ts.API.handler.ServeHTTP(w, req)

		require.Equal(ts.T(), w.Code, example.Code)

		locationURLString := ""

		if example.Code == http.StatusSeeOther {
			locationURLString = w.Header().Get("Location")
		} else if example.Code == http.StatusOK {
			var response struct {
				URL string `json:"url"`
			}

			require.NoError(ts.T(), json.NewDecoder(w.Body).Decode(&response))

			require.NotEmpty(ts.T(), response.URL)

			locationURLString = response.URL
		} else {
			continue
		}

		locationURL, err := url.ParseRequestURI(locationURLString)
		require.NoError(ts.T(), err)

		locationQuery, err := url.ParseQuery(locationURL.RawQuery)

		require.NoError(ts.T(), err)

		samlQueryParams := []string{
			"SAMLRequest",
			"RelayState",
			"SigAlg",
			"Signature",
		}

		for _, param := range samlQueryParams {
			require.True(ts.T(), locationQuery.Has(param))
		}

		for _, param := range samlQueryParams {
			locationQuery.Del(param)
		}

		locationURL.RawQuery = locationQuery.Encode()

		require.Equal(ts.T(), locationURL.String(), example.URL)
	}
}

func TestSSOCreateParamsValidation(t *testing.T) {
	// TODO
}
