package providers

import (
	"net/url"
	"strings"
	"testing"
)

func TestAdditionalProviderStateGeneratorsProduceDistinctTokens(t *testing.T) {
	tests := []struct {
		name          string
		generateState func() (string, error)
	}{
		{
			name: "facebook",
			generateState: func() (string, error) {
				return NewFacebookProvider("app-id", "secret", "https://admin.example.com/callback").GenerateState()
			},
		},
		{
			name: "qq",
			generateState: func() (string, error) {
				return NewQQProvider("app-id", "secret", "https://admin.example.com/callback").GenerateState()
			},
		},
		{
			name: "weibo",
			generateState: func() (string, error) {
				return NewWeiboProvider("app-id", "secret", "https://admin.example.com/callback").GenerateState()
			},
		},
	}

	for _, tc := range tests {
		t.Run(tc.name, func(t *testing.T) {
			stateA, err := tc.generateState()
			if err != nil {
				t.Fatalf("GenerateState(first) failed: %v", err)
			}
			stateB, err := tc.generateState()
			if err != nil {
				t.Fatalf("GenerateState(second) failed: %v", err)
			}
			if stateA == "" || stateB == "" {
				t.Fatal("expected non-empty generated states")
			}
			if stateA == stateB {
				t.Fatal("expected generated states to differ between calls")
			}
		})
	}
}

func TestAdditionalProviderAuthURLs(t *testing.T) {
	tests := []struct {
		name           string
		buildURL       func(t *testing.T) (string, string)
		expectedHost   string
		expectedPath   string
		expectedKey    string
		expectedValue  string
		expectedClause string
	}{
		{
			name: "facebook",
			buildURL: func(t *testing.T) (string, string) {
				t.Helper()
				redirectURI := "https://admin.example.com/login/oauth/callback?from=fb"
				authURL, err := NewFacebookProvider("fb-app-id", "fb-secret", redirectURI).GetAuthURL("fb-state")
				if err != nil {
					t.Fatalf("GetAuthURL failed: %v", err)
				}
				return authURL.URL, redirectURI
			},
			expectedHost:   "www.facebook.com",
			expectedPath:   "/v18.0/dialog/oauth",
			expectedKey:    "client_id",
			expectedValue:  "fb-app-id",
			expectedClause: "scope=email,public_profile",
		},
		{
			name: "qq",
			buildURL: func(t *testing.T) (string, string) {
				t.Helper()
				redirectURI := "https://admin.example.com/login/oauth/callback?from=qq"
				authURL, err := NewQQProvider("qq-app-id", "qq-secret", redirectURI).GetAuthURL("qq-state")
				if err != nil {
					t.Fatalf("GetAuthURL failed: %v", err)
				}
				return authURL.URL, redirectURI
			},
			expectedHost:   "graph.qq.com",
			expectedPath:   "/oauth2.0/authorize",
			expectedKey:    "client_id",
			expectedValue:  "qq-app-id",
			expectedClause: "scope=get_user_info",
		},
		{
			name: "weibo",
			buildURL: func(t *testing.T) (string, string) {
				t.Helper()
				redirectURI := "https://admin.example.com/login/oauth/callback?from=weibo"
				authURL, err := NewWeiboProvider("wb-app-id", "wb-secret", redirectURI).GetAuthURL("wb-state")
				if err != nil {
					t.Fatalf("GetAuthURL failed: %v", err)
				}
				return authURL.URL, redirectURI
			},
			expectedHost:   "api.weibo.com",
			expectedPath:   "/oauth2/authorize",
			expectedKey:    "client_id",
			expectedValue:  "wb-app-id",
			expectedClause: "response_type=code",
		},
		{
			name: "douyin",
			buildURL: func(t *testing.T) (string, string) {
				t.Helper()
				redirectURI := "https://admin.example.com/login/oauth/callback?from=douyin"
				authURL, err := NewDouyinProvider("dy-client", "dy-secret", redirectURI).GetAuthURL("dy-state")
				if err != nil {
					t.Fatalf("GetAuthURL failed: %v", err)
				}
				return authURL, redirectURI
			},
			expectedHost:   "open.douyin.com",
			expectedPath:   "/platform/oauth/connect",
			expectedKey:    "client_key",
			expectedValue:  "dy-client",
			expectedClause: "scope=user_info",
		},
		{
			name: "alipay",
			buildURL: func(t *testing.T) (string, string) {
				t.Helper()
				redirectURI := "https://admin.example.com/login/oauth/callback?from=alipay"
				authURL, err := NewAlipayProvider("ali-app-id", "private-key", redirectURI, false).GetAuthURL("ali-state")
				if err != nil {
					t.Fatalf("GetAuthURL failed: %v", err)
				}
				return authURL, redirectURI
			},
			expectedHost:   "openauth.alipay.com",
			expectedPath:   "/oauth2/publicAppAuthorize.htm",
			expectedKey:    "app_id",
			expectedValue:  "ali-app-id",
			expectedClause: "scope=auth_user",
		},
	}

	for _, tc := range tests {
		t.Run(tc.name, func(t *testing.T) {
			authURL, redirectURI := tc.buildURL(t)

			parsed, err := url.Parse(authURL)
			if err != nil {
				t.Fatalf("parse auth url failed: %v", err)
			}

			if parsed.Host != tc.expectedHost {
				t.Fatalf("expected host %q, got %q", tc.expectedHost, parsed.Host)
			}
			if parsed.Path != tc.expectedPath {
				t.Fatalf("expected path %q, got %q", tc.expectedPath, parsed.Path)
			}

			query := parsed.Query()
			if query.Get(tc.expectedKey) != tc.expectedValue {
				t.Fatalf("expected %s=%q, got %q", tc.expectedKey, tc.expectedValue, query.Get(tc.expectedKey))
			}
			if query.Get("redirect_uri") != redirectURI {
				t.Fatalf("expected redirect_uri %q, got %q", redirectURI, query.Get("redirect_uri"))
			}
			if !strings.Contains(authURL, tc.expectedClause) {
				t.Fatalf("expected auth url to contain %q, got %q", tc.expectedClause, authURL)
			}
		})
	}
}

func TestAlipayProviderUsesExpectedGatewayForSandboxAndProduction(t *testing.T) {
	productionProvider := NewAlipayProvider("prod-app-id", "private-key", "https://admin.example.com/callback", false)
	if gateway := productionProvider.getGateway(); gateway != "https://openapi.alipay.com/gateway.do" {
		t.Fatalf("expected production gateway, got %q", gateway)
	}

	sandboxProvider := NewAlipayProvider("sandbox-app-id", "private-key", "https://admin.example.com/callback", true)
	if gateway := sandboxProvider.getGateway(); gateway != "https://openapi-sandbox.dl.alipaydev.com/gateway.do" {
		t.Fatalf("expected sandbox gateway, got %q", gateway)
	}
}
