package migration

import (
	"encoding/json"
	"io"
	"net/http"
	"net/http/httptest"
	"strings"
	"testing"
)

func TestNormalizeAuthModeMicrosoftApp(t *testing.T) {
	if got := NormalizeAuthMode("microsoft", "microsoft_app"); got != AuthModeMicrosoftApp {
		t.Fatalf("got %q", got)
	}
	if got := NormalizeAuthMode("google", "microsoft_app"); got != AuthModeOAuth {
		t.Fatalf("google ignores ms app: got %q", got)
	}
}

func TestUsesUserOAuth(t *testing.T) {
	if UsesUserOAuth("google", AuthModeGoogleDWD) {
		t.Fatal("google dwd should skip user oauth")
	}
	if UsesUserOAuth("microsoft", AuthModeMicrosoftApp) {
		t.Fatal("microsoft app should skip user oauth")
	}
	if !UsesUserOAuth("microsoft", "oauth") {
		t.Fatal("microsoft oauth needs user oauth")
	}
}

func TestGraphUserBase(t *testing.T) {
	if got := graphUserBase(""); got != "/v1.0/me" {
		t.Fatalf("empty upn: %q", got)
	}
	if got := graphUserBase("alice@contoso.com"); got != "/v1.0/users/alice@contoso.com" {
		t.Fatalf("encoded upn: %q", got)
	}
}

func TestNewMicrosoftAppEmpty(t *testing.T) {
	app, err := NewMicrosoftApp(MicrosoftAppConfig{})
	if err != nil || app != nil {
		t.Fatalf("empty config: app=%v err=%v", app, err)
	}
}

func TestMicrosoftAppAccessToken(t *testing.T) {
	srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		if r.Method != http.MethodPost {
			t.Fatalf("method %s", r.Method)
		}
		if err := r.ParseForm(); err != nil {
			t.Fatal(err)
		}
		if r.Form.Get("grant_type") != "client_credentials" {
			t.Fatalf("grant_type=%q", r.Form.Get("grant_type"))
		}
		auth := r.Header.Get("Authorization")
		if !strings.HasPrefix(auth, "Basic ") {
			t.Fatalf("expected basic auth, got %q", auth)
		}
		if !strings.Contains(r.Form.Get("scope"), "graph.microsoft.com/.default") {
			t.Fatalf("scope=%q", r.Form.Get("scope"))
		}
		w.Header().Set("Content-Type", "application/json")
		_, _ = io.WriteString(w, `{"access_token":"app-token","token_type":"Bearer","expires_in":3600}`)
	}))
	t.Cleanup(srv.Close)

	app, err := NewMicrosoftApp(MicrosoftAppConfig{
		ClientID:     "client-id",
		ClientSecret: "client-secret",
		TokenURL:     srv.URL,
	})
	if err != nil {
		t.Fatal(err)
	}
	token, err := app.AccessToken(t.Context(), "tenant-123")
	if err != nil {
		t.Fatal(err)
	}
	if token != "app-token" {
		t.Fatalf("token=%q", token)
	}
}

func TestMicrosoftAppRequiresTenant(t *testing.T) {
	app, err := NewMicrosoftApp(MicrosoftAppConfig{
		ClientID:     "client-id",
		ClientSecret: "client-secret",
		TokenURL:     "http://example.invalid/token",
	})
	if err != nil {
		t.Fatal(err)
	}
	if _, err := app.AccessToken(t.Context(), ""); err == nil {
		t.Fatal("expected tenant required error")
	}
}

func TestWorkerAuthPathSelection(t *testing.T) {
	cases := []struct {
		provider string
		authMode string
		userOAuth bool
	}{
		{"google", AuthModeOAuth, true},
		{"google", AuthModeGoogleDWD, false},
		{"microsoft", AuthModeOAuth, true},
		{"microsoft", AuthModeMicrosoftApp, false},
		{"microsoft", "google_dwd", true},
	}
	for _, tc := range cases {
		got := UsesUserOAuth(tc.provider, tc.authMode)
		if got != tc.userOAuth {
			t.Fatalf("%s/%s: got userOAuth=%v want %v", tc.provider, tc.authMode, got, tc.userOAuth)
		}
	}
}

func TestMicrosoftAppTokenError(t *testing.T) {
	srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		w.WriteHeader(http.StatusBadRequest)
		_ = json.NewEncoder(w).Encode(map[string]string{"error": "invalid_client"})
	}))
	t.Cleanup(srv.Close)

	app, err := NewMicrosoftApp(MicrosoftAppConfig{
		ClientID:     "bad",
		ClientSecret: "bad",
		TokenURL:     srv.URL,
	})
	if err != nil {
		t.Fatal(err)
	}
	if _, err := app.AccessToken(t.Context(), "tenant"); err == nil {
		t.Fatal("expected token error")
	}
}
