package rest

import (
	"errors"
	"net/http"
	"net/http/httptest"
	"testing"

	"github.com/pingcap/agent-git-service/internal/service"
	"github.com/pingcap/agent-git-service/internal/tenant"
)

func TestConnectedLoginCookiesAreHostOnlyAndUseTenantPolicy(t *testing.T) {
	request := httptest.NewRequest("GET", "http://internal/auth/connected/login", nil)
	request = request.WithContext(tenant.WithInfo(request.Context(), tenant.Info{
		ID:               "tenant-a",
		CanonicalBaseURL: "https://a.example.test",
	}))

	for _, cookie := range []*http.Cookie{
		connectedLoginStateCookie("state", request, false),
		connectedLoginVerifierCookie("verifier", request, false),
	} {
		if cookie.Domain != "" {
			t.Fatalf("%s cookie domain = %q, want host-only", cookie.Name, cookie.Domain)
		}
		if !cookie.Secure {
			t.Fatalf("%s cookie is not secure for an HTTPS tenant", cookie.Name)
		}
		if !cookie.HttpOnly {
			t.Fatalf("%s cookie is not HttpOnly", cookie.Name)
		}
	}
}

func TestConnectedLoginPolicyFailsClosedWithoutTenant(t *testing.T) {
	request := httptest.NewRequest("GET", "http://attacker.example/auth/connected/callback", nil)

	if !connectedLoginCookieSecure(nil) || !connectedLoginCookieSecure(request) {
		t.Fatal("missing tenant produced an insecure connected-login cookie")
	}
	if _, err := (&Deps{}).connectedAuthorizationCodeRedirectURI(request); !errors.Is(err, tenant.ErrRequired) {
		t.Fatalf("redirect policy error = %v, want %v", err, tenant.ErrRequired)
	}
	if target, ok := (&Deps{}).connectedConsoleRedirectURL(request, "code", service.ConnectedSessionResult{}); ok || target != "" {
		t.Fatalf("missing tenant produced console redirect %q", target)
	}
}

func TestConnectedLoginAliasRedirectsToCanonicalHostBeforeSettingState(t *testing.T) {
	request := httptest.NewRequest(http.MethodGet, "https://alias.example.test/auth/connected/login?return_to=console", nil)
	ctx := tenant.WithInfo(request.Context(), tenant.Info{
		ID:               "tenant-a",
		CanonicalBaseURL: "https://canonical.example.test",
	})
	ctx = tenant.WithResolvedHost(ctx, "alias.example.test")
	request = request.WithContext(ctx)

	recorder := httptest.NewRecorder()
	(&Deps{}).ConnectedLogin(recorder, request)

	if recorder.Code != http.StatusFound {
		t.Fatalf("alias login status = %d, want %d", recorder.Code, http.StatusFound)
	}
	if got := recorder.Header().Get("Location"); got != "https://canonical.example.test/auth/connected/login?return_to=console" {
		t.Fatalf("alias login redirect = %q", got)
	}
	if cookies := recorder.Result().Cookies(); len(cookies) != 0 {
		t.Fatalf("alias login set cookies before canonical redirect: %#v", cookies)
	}
}

func TestConnectedLoginCallbackRejectsAliasHost(t *testing.T) {
	request := httptest.NewRequest(http.MethodGet, "https://alias.example.test/auth/connected/callback?code=code&state=state", nil)
	ctx := tenant.WithInfo(request.Context(), tenant.Info{
		ID:               "tenant-a",
		CanonicalBaseURL: "https://canonical.example.test",
	})
	ctx = tenant.WithResolvedHost(ctx, "alias.example.test")
	request = request.WithContext(ctx)

	recorder := httptest.NewRecorder()
	(&Deps{}).ConnectedCallback(recorder, request)

	if recorder.Code != http.StatusMisdirectedRequest {
		t.Fatalf("alias callback status = %d, want %d: %s", recorder.Code, http.StatusMisdirectedRequest, recorder.Body.String())
	}
}
