package providers

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

func TestGitHubProviderGetAuthURLEscapesRedirectAndState(t *testing.T) {
	provider := NewGitHubProvider("client-id", "client-secret", "https://admin.example.com/login/oauth/callback")

	authURL, err := provider.GetAuthURL("state value")
	if err != nil {
		t.Fatalf("GetAuthURL failed: %v", err)
	}

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

	query := parsed.Query()
	if query.Get("client_id") != "client-id" {
		t.Fatalf("expected client_id to be propagated, got %q", query.Get("client_id"))
	}
	if query.Get("redirect_uri") != "https://admin.example.com/login/oauth/callback" {
		t.Fatalf("expected redirect_uri to be propagated, got %q", query.Get("redirect_uri"))
	}
	if query.Get("state") != "state value" {
		t.Fatalf("expected state to be propagated, got %q", query.Get("state"))
	}
	if !strings.Contains(query.Get("scope"), "read:user") {
		t.Fatalf("expected GitHub scope to include read:user, got %q", query.Get("scope"))
	}
}

func TestGoogleProviderGenerateStateAndBuildAuthURL(t *testing.T) {
	provider := NewGoogleProvider("google-client", "google-secret", "https://admin.example.com/login/oauth/callback")

	stateA, err := provider.GenerateState()
	if err != nil {
		t.Fatalf("GenerateState failed: %v", err)
	}
	stateB, err := provider.GenerateState()
	if err != nil {
		t.Fatalf("GenerateState failed: %v", err)
	}

	if stateA == "" || stateB == "" {
		t.Fatal("expected non-empty generated states")
	}
	if stateA == stateB {
		t.Fatal("expected generated states to be unique across calls")
	}

	authURL, err := provider.GetAuthURL("redirect-state")
	if err != nil {
		t.Fatalf("GetAuthURL failed: %v", err)
	}
	if authURL.State != "redirect-state" {
		t.Fatalf("expected auth url state to be preserved, got %q", authURL.State)
	}
	if authURL.Redirect != provider.RedirectURI {
		t.Fatalf("expected redirect uri to be preserved, got %q", authURL.Redirect)
	}
	if !strings.Contains(authURL.URL, "response_type=code") {
		t.Fatalf("expected google auth url to request authorization code flow, got %q", authURL.URL)
	}
}

func TestWeChatProviderGetAuthURLSupportsKnownTypes(t *testing.T) {
	tests := []struct {
		name         string
		oauthType    string
		expectedHost string
		expectedPath string
	}{
		{
			name:         "web login",
			oauthType:    "web",
			expectedHost: "open.weixin.qq.com",
			expectedPath: "/connect/qrconnect",
		},
		{
			name:         "public account login",
			oauthType:    "mp",
			expectedHost: "open.weixin.qq.com",
			expectedPath: "/connect/oauth2/authorize",
		},
	}

	for _, tc := range tests {
		t.Run(tc.name, func(t *testing.T) {
			provider := NewWeChatProvider("wx-app-id", "wx-app-secret", tc.oauthType)
			authURL, err := provider.GetAuthURL("https://admin.example.com/login/oauth/callback", "wechat-state")
			if err != nil {
				t.Fatalf("GetAuthURL failed: %v", err)
			}

			parsed, err := url.Parse(authURL.URL)
			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)
			}
			if authURL.State != "wechat-state" {
				t.Fatalf("expected state to be preserved, got %q", authURL.State)
			}
		})
	}
}

func TestWeChatProviderRejectsUnsupportedOAuthType(t *testing.T) {
	provider := NewWeChatProvider("wx-app-id", "wx-app-secret", "mini")

	if _, err := provider.GetAuthURL("https://admin.example.com/login/oauth/callback", "state"); err == nil {
		t.Fatal("expected unsupported oauth type error")
	}
}
