package auth

import (
	"os"
	"path/filepath"
	"sync"
	"testing"
)

func TestGetEnv(t *testing.T) {
	// Test with default value when env not set
	result := getEnv("NON_EXISTENT_ENV_VAR", "default")
	if result != "default" {
		t.Errorf("getEnv() = %s, want default", result)
	}

	// Test with env set
	os.Setenv("TEST_ENV_VAR", "test_value")
	defer os.Unsetenv("TEST_ENV_VAR")

	result = getEnv("TEST_ENV_VAR", "default")
	if result != "test_value" {
		t.Errorf("getEnv() = %s, want test_value", result)
	}
}

func TestGetEnvBool(t *testing.T) {
	tests := []struct {
		name         string
		envValue     string
		defaultValue bool
		want         bool
	}{
		{"default true, no env", "", true, true},
		{"default false, no env", "", false, false},
		{"env true", "true", false, true},
		{"env TRUE", "TRUE", false, true},
		{"env True", "True", false, true},
		{"env 1", "1", false, true},
		{"env false", "false", true, false},
		{"env 0", "0", true, false},
		{"env other", "random", true, false},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			if tt.envValue != "" {
				os.Setenv("TEST_BOOL_ENV", tt.envValue)
				defer os.Unsetenv("TEST_BOOL_ENV")
			} else {
				os.Unsetenv("TEST_BOOL_ENV")
			}

			result := getEnvBool("TEST_BOOL_ENV", tt.defaultValue)
			if result != tt.want {
				t.Errorf("getEnvBool() = %v, want %v", result, tt.want)
			}
		})
	}
}

func TestLoadFromEnv(t *testing.T) {
	// Set some env vars
	os.Setenv("OAUTH_REDIRECT_BASE_URL", "https://example.com")
	os.Setenv("OAUTH_CALLBACK_PATH", "/auth/callback")
	os.Setenv("WECHAT_OAUTH_ENABLED", "true")
	os.Setenv("WECHAT_APP_ID", "wechat-app-id")
	os.Setenv("GOOGLE_OAUTH_ENABLED", "true")
	os.Setenv("GOOGLE_CLIENT_ID", "google-client-id")
	defer func() {
		os.Unsetenv("OAUTH_REDIRECT_BASE_URL")
		os.Unsetenv("OAUTH_CALLBACK_PATH")
		os.Unsetenv("WECHAT_OAUTH_ENABLED")
		os.Unsetenv("WECHAT_APP_ID")
		os.Unsetenv("GOOGLE_OAUTH_ENABLED")
		os.Unsetenv("GOOGLE_CLIENT_ID")
	}()

	config := loadFromEnv()

	if config.Common.RedirectBaseURL != "https://example.com" {
		t.Errorf("RedirectBaseURL = %s, want https://example.com", config.Common.RedirectBaseURL)
	}
	if config.Common.CallbackPath != "/auth/callback" {
		t.Errorf("CallbackPath = %s, want /auth/callback", config.Common.CallbackPath)
	}
	if !config.WeChat.Enabled {
		t.Error("WeChat.Enabled should be true")
	}
	if config.WeChat.AppID != "wechat-app-id" {
		t.Errorf("WeChat.AppID = %s, want wechat-app-id", config.WeChat.AppID)
	}
	if !config.Google.Enabled {
		t.Error("Google.Enabled should be true")
	}
	if config.Google.ClientID != "google-client-id" {
		t.Errorf("Google.ClientID = %s, want google-client-id", config.Google.ClientID)
	}

	// Check default URLs
	if config.WeChat.AuthURL != "https://open.weixin.qq.com/connect/qrconnect" {
		t.Errorf("WeChat.AuthURL = %s", config.WeChat.AuthURL)
	}
	if config.Google.UserInfoURL != "https://www.googleapis.com/oauth2/v2/userinfo" {
		t.Errorf("Google.UserInfoURL = %s", config.Google.UserInfoURL)
	}
}

// resetOAuthConfig resets the oauth config singleton for testing
func resetOAuthConfig() {
	oauthConfig = nil
	oauthConfigOnce = sync.Once{}
}

func TestLoadOAuthConfig_FileNotExists(t *testing.T) {
	// Reset the singleton for testing
	resetOAuthConfig()

	// Load from non-existent file - should fall back to env
	config, _ := LoadOAuthConfig("/non/existent/path/config.yaml")
	if config == nil {
		t.Error("LoadOAuthConfig() should return config even when file doesn't exist")
	}
}

func TestLoadOAuthConfig_InvalidYAML(t *testing.T) {
	// Create temp file with invalid YAML
	tmpDir := t.TempDir()
	configPath := filepath.Join(tmpDir, "invalid_config.yaml")
	if err := os.WriteFile(configPath, []byte("invalid: yaml: content: ["), 0644); err != nil {
		t.Fatalf("Failed to write temp file: %v", err)
	}

	// Reset the singleton for testing
	resetOAuthConfig()

	config, err := LoadOAuthConfig(configPath)
	if err == nil {
		t.Error("LoadOAuthConfig() should return error for invalid YAML")
	}
	if config == nil {
		t.Error("LoadOAuthConfig() should still return fallback config on error")
	}
}

func TestLoadOAuthConfig_ValidYAML(t *testing.T) {
	yamlContent := `
common:
  redirect_base_url: "https://myapp.com"
  callback_path: "/oauth/callback"
wechat:
  enabled: true
  app_id: "test-wechat-id"
  app_secret: "test-secret"
  scopes:
    - snsapi_login
google:
  enabled: true
  client_id: "test-google-id"
  client_secret: "test-secret"
  scopes:
    - openid
    - email
facebook:
  enabled: false
  app_id: ""
  app_secret: ""
qq:
  enabled: true
  app_id: "test-qq-id"
  app_key: "test-qq-key"
weibo:
  enabled: false
twitter:
  enabled: false
`
	tmpDir := t.TempDir()
	configPath := filepath.Join(tmpDir, "oauth_config.yaml")
	if err := os.WriteFile(configPath, []byte(yamlContent), 0644); err != nil {
		t.Fatalf("Failed to write temp file: %v", err)
	}

	// Reset the singleton for testing
	resetOAuthConfig()

	config, err := LoadOAuthConfig(configPath)
	if err != nil {
		t.Fatalf("LoadOAuthConfig() error = %v", err)
	}

	if config.Common.RedirectBaseURL != "https://myapp.com" {
		t.Errorf("RedirectBaseURL = %s, want https://myapp.com", config.Common.RedirectBaseURL)
	}
	if !config.WeChat.Enabled {
		t.Error("WeChat.Enabled should be true")
	}
	if config.WeChat.AppID != "test-wechat-id" {
		t.Errorf("WeChat.AppID = %s, want test-wechat-id", config.WeChat.AppID)
	}
	if len(config.WeChat.Scopes) != 1 || config.WeChat.Scopes[0] != "snsapi_login" {
		t.Errorf("WeChat.Scopes = %v, want [snsapi_login]", config.WeChat.Scopes)
	}
	if !config.Google.Enabled {
		t.Error("Google.Enabled should be true")
	}
	if len(config.Google.Scopes) != 2 {
		t.Errorf("Google.Scopes length = %d, want 2", len(config.Google.Scopes))
	}
	if config.Facebook.Enabled {
		t.Error("Facebook.Enabled should be false")
	}
	if !config.QQ.Enabled {
		t.Error("QQ.Enabled should be true")
	}
}

func TestGetOAuthConfig(t *testing.T) {
	// Reset the singleton
	resetOAuthConfig()

	// Set an env var to verify it's loaded
	os.Setenv("OAUTH_REDIRECT_BASE_URL", "https://test-get-config.com")
	defer os.Unsetenv("OAUTH_REDIRECT_BASE_URL")

	config := GetOAuthConfig()
	if config == nil {
		t.Fatal("GetOAuthConfig() returned nil")
	}

	if config.Common.RedirectBaseURL != "https://test-get-config.com" {
		t.Errorf("RedirectBaseURL = %s, want https://test-get-config.com", config.Common.RedirectBaseURL)
	}

	// Call again to test singleton behavior
	config2 := GetOAuthConfig()
	if config != config2 {
		t.Error("GetOAuthConfig() should return same instance")
	}
}

func TestLoadOAuthConfig_DefaultPath(t *testing.T) {
	// Reset the singleton
	resetOAuthConfig()

	// Set env to verify fallback to env
	os.Setenv("OAUTH_REDIRECT_BASE_URL", "https://default-path-test.com")
	defer os.Unsetenv("OAUTH_REDIRECT_BASE_URL")

	// Load with empty path - should use default path and fall back to env
	config, _ := LoadOAuthConfig("")

	if config.Common.RedirectBaseURL != "https://default-path-test.com" {
		t.Errorf("RedirectBaseURL = %s, want https://default-path-test.com", config.Common.RedirectBaseURL)
	}
}

func TestMiniProgramConfig(t *testing.T) {
	yamlContent := `
wechat:
  enabled: true
  app_id: "test-app-id"
  mini_program:
    enabled: true
    app_id: "mini-app-id"
    app_secret: "mini-secret"
`
	tmpDir := t.TempDir()
	configPath := filepath.Join(tmpDir, "oauth_config.yaml")
	if err := os.WriteFile(configPath, []byte(yamlContent), 0644); err != nil {
		t.Fatalf("Failed to write temp file: %v", err)
	}

	// Reset the singleton for testing
	resetOAuthConfig()

	config, err := LoadOAuthConfig(configPath)
	if err != nil {
		t.Fatalf("LoadOAuthConfig() error = %v", err)
	}

	if !config.WeChat.MiniProgram.Enabled {
		t.Error("MiniProgram.Enabled should be true")
	}
	if config.WeChat.MiniProgram.AppID != "mini-app-id" {
		t.Errorf("MiniProgram.AppID = %s, want mini-app-id", config.WeChat.MiniProgram.AppID)
	}
}

func TestAllOAuthConfigs_HaveDefaultURLs(t *testing.T) {
	// Clear all relevant env vars
	envVars := []string{
		"WECHAT_AUTH_URL", "WECHAT_TOKEN_URL", "WECHAT_USER_INFO_URL",
		"GOOGLE_AUTH_URL", "GOOGLE_TOKEN_URL", "GOOGLE_USER_INFO_URL",
		"FACEBOOK_AUTH_URL", "FACEBOOK_TOKEN_URL", "FACEBOOK_USER_INFO_URL",
		"QQ_AUTH_URL", "QQ_TOKEN_URL", "QQ_OPENID_URL", "QQ_USER_INFO_URL",
		"WEIBO_AUTH_URL", "WEIBO_TOKEN_URL", "WEIBO_USER_INFO_URL",
		"TWITTER_AUTH_URL", "TWITTER_TOKEN_URL", "TWITTER_USER_INFO_URL",
	}
	for _, v := range envVars {
		os.Unsetenv(v)
	}

	config := loadFromEnv()

	// Verify WeChat defaults
	if config.WeChat.AuthURL != "https://open.weixin.qq.com/connect/qrconnect" {
		t.Errorf("WeChat.AuthURL default incorrect: %s", config.WeChat.AuthURL)
	}

	// Verify Google defaults
	if config.Google.AuthURL != "https://accounts.google.com/o/oauth2/v2/auth" {
		t.Errorf("Google.AuthURL default incorrect: %s", config.Google.AuthURL)
	}

	// Verify Facebook defaults
	if config.Facebook.AuthURL != "https://www.facebook.com/v18.0/dialog/oauth" {
		t.Errorf("Facebook.AuthURL default incorrect: %s", config.Facebook.AuthURL)
	}

	// Verify QQ defaults
	if config.QQ.AuthURL != "https://graph.qq.com/oauth2.0/authorize" {
		t.Errorf("QQ.AuthURL default incorrect: %s", config.QQ.AuthURL)
	}

	// Verify Weibo defaults
	if config.Weibo.AuthURL != "https://api.weibo.com/oauth2/authorize" {
		t.Errorf("Weibo.AuthURL default incorrect: %s", config.Weibo.AuthURL)
	}

	// Verify Twitter defaults
	if config.Twitter.AuthURL != "https://twitter.com/i/oauth2/authorize" {
		t.Errorf("Twitter.AuthURL default incorrect: %s", config.Twitter.AuthURL)
	}
}
