package providers

import (
	"strings"
	"testing"
)

// TestProviderFactory_NewProviderFactory tests the factory constructor
func TestProviderFactory_NewProviderFactory(t *testing.T) {
	factory := NewProviderFactory()

	if factory == nil {
		t.Fatal("Expected factory to be created, got nil")
	}

	if factory.registry == nil {
		t.Error("Expected registry to be initialized")
	}
}

// TestProviderFactory_CreateProvider tests provider creation by issuer URL
func TestProviderFactory_CreateProvider(t *testing.T) {
	factory := NewProviderFactory()

	tests := []struct {
		name         string
		issuerURL    string
		errMsg       string
		expectedType ProviderType
		wantErr      bool
	}{
		{
			name:         "Google provider",
			issuerURL:    "https://accounts.google.com",
			expectedType: ProviderTypeGoogle,
			wantErr:      false,
		},
		{
			name:         "Google provider with path",
			issuerURL:    "https://accounts.google.com/oauth2",
			expectedType: ProviderTypeGoogle,
			wantErr:      false,
		},
		{
			name:         "Azure provider - login.microsoftonline.com",
			issuerURL:    "https://login.microsoftonline.com/tenant-id/v2.0",
			expectedType: ProviderTypeAzure,
			wantErr:      false,
		},
		{
			name:         "Azure provider - sts.windows.net",
			issuerURL:    "https://sts.windows.net/tenant-id",
			expectedType: ProviderTypeAzure,
			wantErr:      false,
		},
		{
			name:         "GitHub provider",
			issuerURL:    "https://github.com/login/oauth",
			expectedType: ProviderTypeGitHub,
			wantErr:      false,
		},
		{
			name:         "Auth0 provider",
			issuerURL:    "https://tenant.auth0.com",
			expectedType: ProviderTypeAuth0,
			wantErr:      false,
		},
		{
			name:         "Okta provider",
			issuerURL:    "https://tenant.okta.com",
			expectedType: ProviderTypeOkta,
			wantErr:      false,
		},
		{
			name:         "Okta preview provider",
			issuerURL:    "https://tenant.oktapreview.com",
			expectedType: ProviderTypeOkta,
			wantErr:      false,
		},
		{
			name:         "Keycloak provider",
			issuerURL:    "https://auth.example.com/auth/realms/master",
			expectedType: ProviderTypeKeycloak,
			wantErr:      false,
		},
		{
			name:         "AWS Cognito provider",
			issuerURL:    "https://cognito-idp.us-east-1.amazonaws.com/us-east-1_example",
			expectedType: ProviderTypeAWSCognito,
			wantErr:      false,
		},
		{
			name:         "GitLab provider",
			issuerURL:    "https://gitlab.com/oauth",
			expectedType: ProviderTypeGitLab,
			wantErr:      false,
		},
		{
			name:         "Generic provider",
			issuerURL:    "https://auth.example.com",
			expectedType: ProviderTypeGeneric,
			wantErr:      false,
		},
		{
			name:      "Empty issuer URL",
			issuerURL: "",
			wantErr:   true,
			errMsg:    "issuer URL cannot be empty",
		},
		{
			name:      "Invalid URL format",
			issuerURL: "not-a-url",
			wantErr:   true,
			errMsg:    "invalid issuer URL format",
		},
		{
			name:      "URL without scheme",
			issuerURL: "example.com",
			wantErr:   true,
			errMsg:    "invalid issuer URL format",
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			provider, err := factory.CreateProvider(tt.issuerURL)

			if tt.wantErr {
				if err == nil {
					t.Error("Expected error but got none")
					return
				}
				if !strings.Contains(err.Error(), tt.errMsg) {
					t.Errorf("Expected error containing '%s', got '%s'", tt.errMsg, err.Error())
				}
				return
			}

			if err != nil {
				t.Errorf("Unexpected error: %v", err)
				return
			}

			if provider == nil {
				t.Fatal("Expected provider to be created, got nil")
			}

			if provider.GetType() != tt.expectedType {
				t.Errorf("Expected provider type %v, got %v", tt.expectedType, provider.GetType())
			}
		})
	}
}

// TestProviderFactory_CreateProviderByType tests provider creation by type
func TestProviderFactory_CreateProviderByType(t *testing.T) {
	factory := NewProviderFactory()

	tests := []struct {
		name         string
		errMsg       string
		providerType ProviderType
		expectedType ProviderType
		wantErr      bool
	}{
		{
			name:         "Generic provider",
			providerType: ProviderTypeGeneric,
			expectedType: ProviderTypeGeneric,
			wantErr:      false,
		},
		{
			name:         "Google provider",
			providerType: ProviderTypeGoogle,
			expectedType: ProviderTypeGoogle,
			wantErr:      false,
		},
		{
			name:         "Azure provider",
			providerType: ProviderTypeAzure,
			expectedType: ProviderTypeAzure,
			wantErr:      false,
		},
		{
			name:         "GitHub provider",
			providerType: ProviderTypeGitHub,
			expectedType: ProviderTypeGitHub,
			wantErr:      false,
		},
		{
			name:         "Auth0 provider",
			providerType: ProviderTypeAuth0,
			expectedType: ProviderTypeAuth0,
			wantErr:      false,
		},
		{
			name:         "Okta provider",
			providerType: ProviderTypeOkta,
			expectedType: ProviderTypeOkta,
			wantErr:      false,
		},
		{
			name:         "Keycloak provider",
			providerType: ProviderTypeKeycloak,
			expectedType: ProviderTypeKeycloak,
			wantErr:      false,
		},
		{
			name:         "AWS Cognito provider",
			providerType: ProviderTypeAWSCognito,
			expectedType: ProviderTypeAWSCognito,
			wantErr:      false,
		},
		{
			name:         "GitLab provider",
			providerType: ProviderTypeGitLab,
			expectedType: ProviderTypeGitLab,
			wantErr:      false,
		},
		{
			name:         "Invalid provider type",
			providerType: ProviderType(999),
			wantErr:      true,
			errMsg:       "unsupported provider type",
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			provider, err := factory.CreateProviderByType(tt.providerType)

			if tt.wantErr {
				if err == nil {
					t.Error("Expected error but got none")
					return
				}
				if !strings.Contains(err.Error(), tt.errMsg) {
					t.Errorf("Expected error containing '%s', got '%s'", tt.errMsg, err.Error())
				}
				return
			}

			if err != nil {
				t.Errorf("Unexpected error: %v", err)
				return
			}

			if provider == nil {
				t.Fatal("Expected provider to be created, got nil")
			}

			if provider.GetType() != tt.expectedType {
				t.Errorf("Expected provider type %v, got %v", tt.expectedType, provider.GetType())
			}
		})
	}
}

// TestProviderFactory_GetSupportedProviders tests listing supported providers
func TestProviderFactory_GetSupportedProviders(t *testing.T) {
	factory := NewProviderFactory()
	supported := factory.GetSupportedProviders()

	// Verify expected provider types are present
	expectedTypes := []ProviderType{
		ProviderTypeGeneric,
		ProviderTypeGoogle,
		ProviderTypeAzure,
	}

	for _, expectedType := range expectedTypes {
		if _, exists := supported[expectedType]; !exists {
			t.Errorf("Expected provider type %v to be supported", expectedType)
		}
	}

	// Verify Google patterns
	googlePatterns := supported[ProviderTypeGoogle]
	if len(googlePatterns) != 1 || googlePatterns[0] != "accounts.google.com" {
		t.Errorf("Expected Google patterns ['accounts.google.com'], got %v", googlePatterns)
	}

	// Verify Azure patterns
	azurePatterns := supported[ProviderTypeAzure]
	expectedAzurePatterns := []string{"login.microsoftonline.com", "sts.windows.net"}
	if len(azurePatterns) != len(expectedAzurePatterns) {
		t.Errorf("Expected %d Azure patterns, got %d", len(expectedAzurePatterns), len(azurePatterns))
	}

	for _, expectedPattern := range expectedAzurePatterns {
		found := false
		for _, pattern := range azurePatterns {
			if pattern == expectedPattern {
				found = true
				break
			}
		}
		if !found {
			t.Errorf("Expected Azure pattern '%s' not found", expectedPattern)
		}
	}

	// Verify Generic patterns
	genericPatterns := supported[ProviderTypeGeneric]
	if len(genericPatterns) != 1 || genericPatterns[0] != "*" {
		t.Errorf("Expected Generic patterns ['*'], got %v", genericPatterns)
	}
}

// TestProviderFactory_DetectProviderType tests provider type detection
func TestProviderFactory_DetectProviderType(t *testing.T) {
	factory := NewProviderFactory()

	tests := []struct {
		name         string
		issuerURL    string
		expectedType ProviderType
		wantErr      bool
	}{
		{
			name:         "Google provider detection",
			issuerURL:    "https://accounts.google.com",
			expectedType: ProviderTypeGoogle,
			wantErr:      false,
		},
		{
			name:         "Azure provider detection",
			issuerURL:    "https://login.microsoftonline.com/tenant/v2.0",
			expectedType: ProviderTypeAzure,
			wantErr:      false,
		},
		{
			name:         "Generic provider detection",
			issuerURL:    "https://auth.example.com",
			expectedType: ProviderTypeGeneric,
			wantErr:      false,
		},
		{
			name:      "Invalid URL",
			issuerURL: "not-a-url",
			wantErr:   true,
		},
		{
			name:      "Empty URL",
			issuerURL: "",
			wantErr:   true,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			providerType, err := factory.DetectProviderType(tt.issuerURL)

			if tt.wantErr {
				if err == nil {
					t.Error("Expected error but got none")
				}
				return
			}

			if err != nil {
				t.Errorf("Unexpected error: %v", err)
				return
			}

			if providerType != tt.expectedType {
				t.Errorf("Expected provider type %v, got %v", tt.expectedType, providerType)
			}
		})
	}
}

// TestProviderFactory_IsProviderSupported tests provider support checking
func TestProviderFactory_IsProviderSupported(t *testing.T) {
	factory := NewProviderFactory()

	tests := []struct {
		name      string
		issuerURL string
		expected  bool
	}{
		{
			name:      "Google provider supported",
			issuerURL: "https://accounts.google.com",
			expected:  true,
		},
		{
			name:      "Google provider with subdomain supported",
			issuerURL: "https://accounts.google.com/oauth2",
			expected:  true,
		},
		{
			name:      "Azure login.microsoftonline.com supported",
			issuerURL: "https://login.microsoftonline.com/tenant/v2.0",
			expected:  true,
		},
		{
			name:      "Azure sts.windows.net supported",
			issuerURL: "https://sts.windows.net/tenant",
			expected:  true,
		},
		{
			name:      "Generic provider supported (wildcard)",
			issuerURL: "https://auth.example.com",
			expected:  true,
		},
		{
			name:      "Any valid URL supported (wildcard)",
			issuerURL: "https://custom-auth.company.org",
			expected:  true,
		},
		{
			name:      "Empty URL not supported",
			issuerURL: "",
			expected:  false,
		},
		{
			name:      "Invalid URL format not supported",
			issuerURL: "not-a-url",
			expected:  false,
		},
		{
			name:      "URL without scheme not supported",
			issuerURL: "example.com",
			expected:  false,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			result := factory.IsProviderSupported(tt.issuerURL)

			if result != tt.expected {
				t.Errorf("Expected %v, got %v", tt.expected, result)
			}
		})
	}
}

// TestProviderFactory_IntegrationTest tests the full flow
func TestProviderFactory_IntegrationTest(t *testing.T) {
	factory := NewProviderFactory()

	// Test Google provider flow
	t.Run("Google Provider Flow", func(t *testing.T) {
		// Check if supported
		if !factory.IsProviderSupported("https://accounts.google.com") {
			t.Error("Google provider should be supported")
		}

		// Detect type
		providerType, err := factory.DetectProviderType("https://accounts.google.com")
		if err != nil {
			t.Errorf("Unexpected error detecting Google provider: %v", err)
		}
		if providerType != ProviderTypeGoogle {
			t.Errorf("Expected ProviderTypeGoogle, got %v", providerType)
		}

		// Create provider by URL
		provider, err := factory.CreateProvider("https://accounts.google.com")
		if err != nil {
			t.Errorf("Unexpected error creating Google provider: %v", err)
		}
		if provider.GetType() != ProviderTypeGoogle {
			t.Errorf("Expected ProviderTypeGoogle, got %v", provider.GetType())
		}

		// Create provider by type
		provider2, err := factory.CreateProviderByType(ProviderTypeGoogle)
		if err != nil {
			t.Errorf("Unexpected error creating Google provider by type: %v", err)
		}
		if provider2.GetType() != ProviderTypeGoogle {
			t.Errorf("Expected ProviderTypeGoogle, got %v", provider2.GetType())
		}
	})

	// Test Azure provider flow
	t.Run("Azure Provider Flow", func(t *testing.T) {
		azureURL := "https://login.microsoftonline.com/tenant/v2.0"

		// Check if supported
		if !factory.IsProviderSupported(azureURL) {
			t.Error("Azure provider should be supported")
		}

		// Detect type
		providerType, err := factory.DetectProviderType(azureURL)
		if err != nil {
			t.Errorf("Unexpected error detecting Azure provider: %v", err)
		}
		if providerType != ProviderTypeAzure {
			t.Errorf("Expected ProviderTypeAzure, got %v", providerType)
		}

		// Create provider
		provider, err := factory.CreateProvider(azureURL)
		if err != nil {
			t.Errorf("Unexpected error creating Azure provider: %v", err)
		}
		if provider.GetType() != ProviderTypeAzure {
			t.Errorf("Expected ProviderTypeAzure, got %v", provider.GetType())
		}
	})

	// Test Generic provider flow
	t.Run("Generic Provider Flow", func(t *testing.T) {
		genericURL := "https://auth.custom-provider.com"

		// Check if supported
		if !factory.IsProviderSupported(genericURL) {
			t.Error("Generic provider should be supported")
		}

		// Detect type
		providerType, err := factory.DetectProviderType(genericURL)
		if err != nil {
			t.Errorf("Unexpected error detecting generic provider: %v", err)
		}
		if providerType != ProviderTypeGeneric {
			t.Errorf("Expected ProviderTypeGeneric, got %v", providerType)
		}

		// Create provider
		provider, err := factory.CreateProvider(genericURL)
		if err != nil {
			t.Errorf("Unexpected error creating generic provider: %v", err)
		}
		if provider.GetType() != ProviderTypeGeneric {
			t.Errorf("Expected ProviderTypeGeneric, got %v", provider.GetType())
		}
	})
}

// TestProviderFactory_CaseInsensitiveHostMatching tests case insensitive host matching
func TestProviderFactory_CaseInsensitiveHostMatching(t *testing.T) {
	factory := NewProviderFactory()

	tests := []struct {
		name         string
		issuerURL    string
		expectedType ProviderType
	}{
		{
			name:         "Google with uppercase",
			issuerURL:    "https://ACCOUNTS.GOOGLE.COM",
			expectedType: ProviderTypeGoogle,
		},
		{
			name:         "Google with mixed case",
			issuerURL:    "https://Accounts.Google.Com",
			expectedType: ProviderTypeGoogle,
		},
		{
			name:         "Azure with uppercase",
			issuerURL:    "https://LOGIN.MICROSOFTONLINE.COM/tenant",
			expectedType: ProviderTypeAzure,
		},
		{
			name:         "Azure STS with mixed case",
			issuerURL:    "https://Sts.Windows.Net/tenant",
			expectedType: ProviderTypeAzure,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			// Should be supported
			if !factory.IsProviderSupported(tt.issuerURL) {
				t.Errorf("URL %s should be supported", tt.issuerURL)
			}

			// Should detect correct type
			providerType, err := factory.DetectProviderType(tt.issuerURL)
			if err != nil {
				t.Errorf("Unexpected error: %v", err)
			}
			if providerType != tt.expectedType {
				t.Errorf("Expected %v, got %v", tt.expectedType, providerType)
			}

			// Should create correct provider
			provider, err := factory.CreateProvider(tt.issuerURL)
			if err != nil {
				t.Errorf("Unexpected error: %v", err)
			}
			if provider.GetType() != tt.expectedType {
				t.Errorf("Expected %v, got %v", tt.expectedType, provider.GetType())
			}
		})
	}
}

// Benchmark tests
func BenchmarkProviderFactory_CreateProvider(b *testing.B) {
	factory := NewProviderFactory()
	issuerURL := "https://accounts.google.com"

	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		factory.CreateProvider(issuerURL)
	}
}

func BenchmarkProviderFactory_IsProviderSupported(b *testing.B) {
	factory := NewProviderFactory()
	issuerURL := "https://auth.example.com"

	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		factory.IsProviderSupported(issuerURL)
	}
}

func BenchmarkProviderFactory_DetectProviderType(b *testing.B) {
	factory := NewProviderFactory()
	issuerURL := "https://login.microsoftonline.com/tenant"

	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		factory.DetectProviderType(issuerURL)
	}
}
