package router

import (
	"context"
	"encoding/base64"
	"encoding/json"
	"net/http"
	"net/http/httptest"
	"strings"
	"testing"
	"time"

	"github.com/stretchr/testify/require"

	"github.com/tidbcloud/AgentTeam/internal/api"
	"github.com/tidbcloud/AgentTeam/internal/api/meshxapi"
	"github.com/tidbcloud/AgentTeam/internal/api/roles"
	"github.com/tidbcloud/AgentTeam/internal/config"
	"github.com/tidbcloud/AgentTeam/internal/identity/trusted"
	kbservice "github.com/tidbcloud/AgentTeam/internal/service/kb"
	"github.com/tidbcloud/AgentTeam/internal/service/resource/repos"
	"github.com/tidbcloud/AgentTeam/internal/store/kb"
	reposstore "github.com/tidbcloud/AgentTeam/internal/store/resource/repos"
	"github.com/tidbcloud/AgentTeam/internal/store/workspace"
	workspaceroute "github.com/tidbcloud/AgentTeam/internal/workspace/router"
	"gorm.io/driver/sqlite"
	"gorm.io/gorm"
)

type testWorkspaceChecker struct {
	owned   map[string]bool
	seenCtx context.Context
	calls   int
	err     error
}

type testRepositoryChecker struct {
	bound       bool
	calls       int
	workspaceID string
	repository  string
	err         error
}

type testRepositoryMutationChecker struct {
	*testRepositoryChecker
	allowed       bool
	mutationErr   error
	mutationCalls int
	mutationRepo  string
}

type testWorkspaceResolver struct {
	target      workspace.WorkspaceRouteTarget
	workspaceID string
	calls       int
	err         error
}

func (resolver *testWorkspaceResolver) ResolveWorkspaceRouteTarget(_ context.Context, workspaceID string) (workspace.WorkspaceRouteTarget, error) {
	resolver.calls++
	resolver.workspaceID = workspaceID
	return resolver.target, resolver.err
}

func (checker *testRepositoryChecker) IsBoundToWorkspace(_ context.Context, workspaceID, repository string) (bool, error) {
	checker.calls++
	checker.workspaceID = workspaceID
	checker.repository = repository
	return checker.bound, checker.err
}

func (checker *testRepositoryMutationChecker) AllowLegacyRepositoryMutation(_ context.Context, _ string, repository string) (bool, error) {
	checker.mutationCalls++
	checker.mutationRepo = repository
	return checker.allowed, checker.mutationErr
}

func (checker *testWorkspaceChecker) IsWorkspaceRoutableToGroup(ctx context.Context, workspaceID, _ string) (bool, error) {
	checker.calls++
	checker.seenCtx = ctx
	return checker.owned[workspaceID], checker.err
}

func TestWorkspaceAGSSeamOwnershipCancellationFailsClosed(t *testing.T) {
	t.Parallel()

	ctx, cancel := context.WithCancel(context.Background())
	cancel()
	checker := &testWorkspaceChecker{owned: map[string]bool{"wsp_1": true}, err: context.Canceled}
	nextCalls := 0
	authority := workspaceAGSAuthority{groupID: "group_1", workspaces: checker, repositories: &testRepositoryChecker{bound: true}, next: http.HandlerFunc(func(http.ResponseWriter, *http.Request) { nextCalls++ })}
	handler := workspaceAGSSeam(config.ResourceRuntimeConfig{PublicBaseURL: "https://deploy.example.test"}, authority, http.NotFoundHandler())
	request := httptest.NewRequest(http.MethodGet, "/api/v1/workspaces/wsp_1/ags/api/v3/repos/acme/docs", nil).WithContext(ctx)
	request.Header.Set(api.HeaderRouteWorkspaceID, "wsp_1")
	request.Header.Set(workspaceroute.HeaderResolvedWorkspaceGroupID, "group_1")
	recorder := httptest.NewRecorder()

	handler.ServeHTTP(recorder, request)

	require.Equal(t, http.StatusBadRequest, recorder.Code)
	require.Equal(t, 1, checker.calls)
	require.Equal(t, ctx, checker.seenCtx)
	require.Zero(t, nextCalls)
}

func TestWorkspaceAGSSeamStripsOnlyTrustedPlacedRequest(t *testing.T) {
	t.Parallel()

	type contextKey struct{}
	ctx, cancel := context.WithCancel(context.WithValue(context.Background(), contextKey{}, "marker"))
	defer cancel()
	done := ctx.Done()
	called := 0
	next := http.HandlerFunc(func(writer http.ResponseWriter, request *http.Request) {
		called++
		require.Equal(t, "/api/v3/repos/acme/docs", request.URL.Path)
		require.Equal(t, "service=git-upload-pack", request.URL.RawQuery)
		require.Equal(t, "marker", request.Context().Value(contextKey{}))
		require.Equal(t, done, request.Context().Done())
		writer.WriteHeader(http.StatusNoContent)
	})
	checker := &testWorkspaceChecker{owned: map[string]bool{"wsp_1": true}}
	authority := workspaceAGSAuthority{groupID: "group_1", workspaces: checker, repositories: &testRepositoryChecker{bound: true}, next: next}
	handler := workspaceAGSSeam(config.ResourceRuntimeConfig{PublicBaseURL: "https://deploy.example.test/console"}, authority, http.NotFoundHandler())

	request := httptest.NewRequest(http.MethodGet, "/api/v1/workspaces/wsp_1/ags/api/v3/repos/acme/docs?service=git-upload-pack", nil).WithContext(ctx)
	request.Host = "attacker.example"
	request.Header.Set("Forwarded", "host=attacker.example")
	request.Header.Set(api.HeaderRouteWorkspaceID, "wsp_1")
	request.Header.Set(workspaceroute.HeaderResolvedWorkspaceGroupID, "group_1")
	recorder := httptest.NewRecorder()
	handler.ServeHTTP(recorder, request)

	require.Equal(t, http.StatusNoContent, recorder.Code)
	require.Equal(t, 1, called)
	require.Equal(t, 1, checker.calls)
	require.Equal(t, ctx, checker.seenCtx)
}

func TestStandaloneWorkspaceAGSSeamResolvesPrimaryRepositoryReads(t *testing.T) {
	t.Parallel()

	resolver := &testWorkspaceResolver{target: workspace.WorkspaceRouteTarget{
		WorkspaceID: "wsp_1", WorkspaceGroupID: "group_actual", PlacementState: workspace.PlacementStateActive,
	}}
	repositories := &testRepositoryChecker{bound: true}
	var seenPath, seenQuery string
	next := http.HandlerFunc(func(writer http.ResponseWriter, request *http.Request) {
		seenPath = request.URL.Path
		seenQuery = request.URL.RawQuery
		require.Equal(t, "wsp_1", request.Header.Get(api.HeaderRouteWorkspaceID))
		require.Equal(t, "wsp_1", request.Header.Get(api.HeaderWorkspaceID))
		require.Equal(t, "group_actual", request.Header.Get(workspaceroute.HeaderResolvedWorkspaceGroupID))
		writer.WriteHeader(http.StatusNoContent)
	})
	handler := StandaloneWorkspaceAGSSeam(
		config.ResourceRuntimeConfig{PublicBaseURL: "https://deploy.example.test"},
		resolver, repositories, next, http.NotFoundHandler(),
	)

	tests := []struct {
		name     string
		resolved string
		native   string
		query    string
	}{
		{name: "repository detail", resolved: "/api/v1/workspaces/wsp_1/ags/api/v3/repos/acme/docs", native: "/api/v3/repos/acme/docs"},
		{name: "readme", resolved: "/api/v1/workspaces/wsp_1/ags/api/v3/repos/acme/docs/readme?ref=main", native: "/api/v3/repos/acme/docs/readme", query: "ref=main"},
		{name: "skill contents", resolved: "/api/v1/workspaces/wsp_1/ags/api/v3/repos/acme/docs/contents/skills/docs/SKILL.md?ref=main", native: "/api/v3/repos/acme/docs/contents/skills/docs/SKILL.md", query: "ref=main"},
		{name: "branches", resolved: "/api/v1/workspaces/wsp_1/ags/api/v3/repos/acme/docs/branches?page=1&per_page=1", native: "/api/v3/repos/acme/docs/branches", query: "page=1&per_page=1"},
	}
	for _, test := range tests {
		t.Run(test.name, func(t *testing.T) {
			seenPath, seenQuery = "", ""
			request := httptest.NewRequest(http.MethodGet, test.resolved, nil)
			request.Header.Set(api.HeaderRouteWorkspaceID, "wsp_forged")
			request.Header.Set(api.HeaderWorkspaceID, "wsp_forged")
			request.Header.Set(workspaceroute.HeaderResolvedWorkspaceGroupID, "group_forged")
			recorder := httptest.NewRecorder()

			handler.ServeHTTP(recorder, request)

			require.Equal(t, http.StatusNoContent, recorder.Code, recorder.Body.String())
			require.Equal(t, test.native, seenPath)
			require.Equal(t, test.query, seenQuery)
		})
	}
	require.Equal(t, len(tests), resolver.calls)
	require.Equal(t, "wsp_1", resolver.workspaceID)
	require.Equal(t, len(tests), repositories.calls)
	require.Equal(t, "wsp_1", repositories.workspaceID)
	require.Equal(t, "acme/docs", repositories.repository)
}

func TestWorkspaceAGSSeamRejectsUnboundRepositoryBeforeAGS(t *testing.T) {
	workspaceChecker := &testWorkspaceChecker{owned: map[string]bool{"wsp_1": true}}
	repositoryChecker := &testRepositoryChecker{}
	nextCalls := 0
	handler := workspaceAGSSeam(config.ResourceRuntimeConfig{PublicBaseURL: "https://deploy.example.test"}, workspaceAGSAuthority{
		groupID: "group_1", workspaces: workspaceChecker, repositories: repositoryChecker,
		next: http.HandlerFunc(func(http.ResponseWriter, *http.Request) { nextCalls++ }),
	}, http.NotFoundHandler())
	request := httptest.NewRequest(http.MethodGet, "/api/v1/workspaces/wsp_1/ags/api/v3/repos/acme/docs", nil)
	request.Header.Set(api.HeaderRouteWorkspaceID, "wsp_1")
	request.Header.Set(workspaceroute.HeaderResolvedWorkspaceGroupID, "group_1")
	recorder := httptest.NewRecorder()

	handler.ServeHTTP(recorder, request)

	require.Equal(t, http.StatusNotFound, recorder.Code)
	require.Equal(t, 1, workspaceChecker.calls)
	require.Equal(t, 1, repositoryChecker.calls)
	require.Equal(t, "wsp_1", repositoryChecker.workspaceID)
	require.Equal(t, "acme/docs", repositoryChecker.repository)
	require.Zero(t, nextCalls)
}

func TestWorkspaceAGSSeamServesSyntheticRawThroughRegisteredContents(t *testing.T) {
	repositoryChecker := &testRepositoryChecker{bound: true}
	nextCalls := 0
	handler := workspaceAGSSeam(config.ResourceRuntimeConfig{PublicBaseURL: "https://deploy.example.test"}, workspaceAGSAuthority{
		groupID: "group_1", workspaces: &testWorkspaceChecker{owned: map[string]bool{"wsp_1": true}}, repositories: repositoryChecker,
		next: http.HandlerFunc(func(writer http.ResponseWriter, request *http.Request) {
			nextCalls++
			require.Equal(t, "/api/v3/repos/acme/docs/contents/guides/README.md", request.URL.Path)
			require.Equal(t, "release/1", request.URL.Query().Get("ref"))
			writer.Header().Set("Content-Type", "application/json")
			_, _ = writer.Write([]byte(`{"name":"README.md","type":"file","encoding":"base64","content":"aGVsbG8K"}`))
		}),
	}, http.NotFoundHandler())
	request := httptest.NewRequest(http.MethodGet, "/api/v1/workspaces/wsp_1/ags/acme/docs/raw/release%2F1/guides/README.md", nil)
	request.Header.Set(api.HeaderRouteWorkspaceID, "wsp_1")
	request.Header.Set(workspaceroute.HeaderResolvedWorkspaceGroupID, "group_1")
	recorder := httptest.NewRecorder()

	handler.ServeHTTP(recorder, request)

	require.Equal(t, http.StatusOK, recorder.Code, recorder.Body.String())
	require.Equal(t, "hello\n", recorder.Body.String())
	require.Equal(t, 1, repositoryChecker.calls)
	require.Equal(t, 1, nextCalls)
}

func TestWorkspaceAGSSeamRejectsUnactivatedRouteBeforeAuthority(t *testing.T) {
	workspaceChecker := &testWorkspaceChecker{owned: map[string]bool{"wsp_1": true}}
	repositoryChecker := &testRepositoryChecker{bound: true}
	nextCalls := 0
	handler := workspaceAGSSeam(config.ResourceRuntimeConfig{PublicBaseURL: "https://deploy.example.test"}, workspaceAGSAuthority{
		groupID: "group_1", workspaces: workspaceChecker, repositories: repositoryChecker,
		next: http.HandlerFunc(func(http.ResponseWriter, *http.Request) { nextCalls++ }),
	}, http.NotFoundHandler())

	for _, target := range []string{
		"/api/v1/workspaces/wsp_1/ags/api/v3/user",
		"/api/v1/workspaces/wsp_1/ags/api/ext/v1/user/tokens",
		"/api/v1/workspaces/wsp_1/ags/api/v3/openapi.json",
		"/api/v1/workspaces/wsp_1/ags/api/ext/v1/analytics/events",
		"/api/v1/workspaces/wsp_1/ags/avatars/alice/extra",
	} {
		recorder := httptest.NewRecorder()
		request := httptest.NewRequest(http.MethodGet, target, nil)
		request.Header.Set(api.HeaderRouteWorkspaceID, "wsp_1")
		request.Header.Set(workspaceroute.HeaderResolvedWorkspaceGroupID, "group_1")
		handler.ServeHTTP(recorder, request)
		require.Equal(t, http.StatusNotFound, recorder.Code, target)
	}
	require.Zero(t, workspaceChecker.calls)
	require.Zero(t, repositoryChecker.calls)
	require.Zero(t, nextCalls)
}

func TestWorkspaceAGSSeamFailsBeforeMutation(t *testing.T) {
	t.Parallel()

	nextCalls := 0
	authority := workspaceAGSAuthority{groupID: "group_b", workspaces: &testWorkspaceChecker{owned: map[string]bool{"wsp_a": true, "wsp_b": true}}, repositories: &testRepositoryChecker{bound: true}, next: http.HandlerFunc(func(http.ResponseWriter, *http.Request) {
		nextCalls++
	})}
	handler := workspaceAGSSeam(config.ResourceRuntimeConfig{PublicBaseURL: "https://deploy.example.test"}, authority, http.NotFoundHandler())
	request := httptest.NewRequest(http.MethodGet, "/api/v1/workspaces/wsp_a/ags/api/v3/repos/acme/docs?ref=main", nil)
	request.Header.Set(api.HeaderRouteWorkspaceID, "wsp_b")
	request.Header.Set(workspaceroute.HeaderResolvedWorkspaceGroupID, "group_b")
	beforeURL, beforeRequestURI := *request.URL, request.RequestURI
	recorder := httptest.NewRecorder()

	handler.ServeHTTP(recorder, request)

	require.Equal(t, http.StatusBadRequest, recorder.Code)
	require.Zero(t, nextCalls)
	require.Equal(t, beforeURL, *request.URL)
	require.Equal(t, beforeRequestURI, request.RequestURI)
}

func TestWorkspaceAGSSeamRequiresLocalGroupAndManagedWorkspace(t *testing.T) {
	t.Parallel()

	for _, test := range []struct {
		name    string
		group   string
		checker workspaceAGSWorkspaceChecker
	}{
		{name: "wrong group", group: "group_other", checker: &testWorkspaceChecker{owned: map[string]bool{"wsp_1": true}}},
		{name: "unmanaged workspace", group: "group_1", checker: &testWorkspaceChecker{owned: map[string]bool{"wsp_other": true}}},
		{name: "missing managed authority", group: "group_1"},
	} {
		t.Run(test.name, func(t *testing.T) {
			nextCalls := 0
			authority := workspaceAGSAuthority{groupID: test.group, workspaces: test.checker, repositories: &testRepositoryChecker{bound: true}, next: http.HandlerFunc(func(http.ResponseWriter, *http.Request) { nextCalls++ })}
			handler := workspaceAGSSeam(config.ResourceRuntimeConfig{PublicBaseURL: "https://deploy.example.test"}, authority, http.NotFoundHandler())
			request := httptest.NewRequest(http.MethodGet, "/api/v1/workspaces/wsp_1/ags/api/v3/repos/acme/docs", nil)
			request.Header.Set(api.HeaderRouteWorkspaceID, "wsp_1")
			request.Header.Set(workspaceroute.HeaderResolvedWorkspaceGroupID, "group_1")
			recorder := httptest.NewRecorder()

			handler.ServeHTTP(recorder, request)

			require.Equal(t, http.StatusBadRequest, recorder.Code)
			require.Zero(t, nextCalls)
		})
	}
}

func TestRepositoryOwnersRouteReturnsRawPersonalAndWorkspaceOwners(t *testing.T) {
	handler := Register(
		config.ResourceRuntimeConfig{Auth: config.ResourceRuntimeAuthConfig{Enabled: true, Header: "Authorization"}},
		http.NotFoundHandler(),
		nil,
		nil,
		nil,
		nil,
		nil,
		nil,
		nil,
		nil,
	)

	req := httptest.NewRequest(http.MethodGet, "/api/v1/repository-owners", nil)
	req.Header.Set("Authorization", "Bearer "+routerTestJWT(map[string]any{"sub": "usr_raw_1"}))
	req.Header.Set(meshxapi.HeaderWorkspaceID, "wsp_raw_1")
	rec := httptest.NewRecorder()

	handler.ServeHTTP(rec, req)

	require.Equal(t, http.StatusOK, rec.Code, rec.Body.String())
	require.JSONEq(t, `{"personalOwnerLogin":"usr_raw_1","workspaceOwnerLogin":"wsp_raw_1"}`, rec.Body.String())
}

func TestMCPPlaceholderPreservesAuthenticatedNotImplementedContract(t *testing.T) {
	handler := Register(
		config.ResourceRuntimeConfig{Auth: config.ResourceRuntimeAuthConfig{Enabled: true, Header: "Authorization"}},
		http.NotFoundHandler(),
		nil, nil, nil, nil, nil, nil, nil, nil,
	)

	for _, path := range []string{"/api/v1/mcp", "/api/v1/mcp/tools"} {
		t.Run(path, func(t *testing.T) {
			request := httptest.NewRequest(http.MethodGet, path, nil)
			request.Header.Set("Authorization", "Bearer "+routerTestJWT(map[string]any{"sub": "usr_mcp"}))
			recorder := httptest.NewRecorder()

			handler.ServeHTTP(recorder, request)

			require.Equal(t, http.StatusNotImplemented, recorder.Code)
			require.JSONEq(t, `{"error":"not_implemented","domain":"mcp","subject":"usr_mcp"}`, recorder.Body.String())

			unauthorized := httptest.NewRecorder()
			handler.ServeHTTP(unauthorized, httptest.NewRequest(http.MethodGet, path, nil))
			require.Equal(t, http.StatusUnauthorized, unauthorized.Code)
		})
	}
}

func TestRegisterWithoutRoleOperationHandlerDoesNotExposeHiddenRoutes(t *testing.T) {
	handler := Register(
		config.ResourceRuntimeConfig{Auth: config.ResourceRuntimeAuthConfig{Enabled: true, Header: "Authorization"}},
		http.NotFoundHandler(),
		nil, nil, nil, nil, nil, nil, nil, nil,
		WithRoleOperations(nil),
	)
	claims := map[string]any{
		"sub":                          "usr_internal",
		"agentteam_internal_purpose":        roles.RoleOperationInternalPurpose,
		"agent_ownership_workspace_id": "wsp_1",
	}
	for _, path := range []string{
		roles.RoleOperationPreflightPath,
		roles.RoleOperationExecutePath,
		roles.RoleOperationRecoveryPath,
	} {
		request := httptest.NewRequest(http.MethodPost, path, strings.NewReader(`{`))
		request.Header.Set("Authorization", "Bearer "+routerTestJWT(claims))
		request.Header.Set(meshxapi.HeaderWorkspaceID, "wsp_1")
		recorder := httptest.NewRecorder()

		handler.ServeHTTP(recorder, request)

		require.Equal(t, http.StatusNotFound, recorder.Code, path)
	}
}

func routerTestJWT(claims map[string]any) string {
	header := base64.RawURLEncoding.EncodeToString([]byte(`{"alg":"none"}`))
	payload, _ := json.Marshal(claims)
	return header + "." + base64.RawURLEncoding.EncodeToString(payload) + "."
}

func TestRepositoryCredentialBypassesSessionJWTOnlyForRepositoryDataPlane(t *testing.T) {
	called := false
	handler := Register(
		config.ResourceRuntimeConfig{Auth: config.ResourceRuntimeAuthConfig{Enabled: true, Header: "Authorization"}},
		http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
			called = true
			w.WriteHeader(http.StatusNoContent)
		}),
		nil, nil, nil, nil, nil, nil, nil, nil,
	)

	for _, authorization := range []string{"Bearer ags_rat_example", "token existing-ags-token"} {
		called = false
		req := httptest.NewRequest(http.MethodGet, "/api/v3/repos/pingcap/agentteam", nil)
		req.Header.Set("Authorization", authorization)
		rec := httptest.NewRecorder()
		handler.ServeHTTP(rec, req)

		require.True(t, called, authorization)
		require.Equal(t, http.StatusNoContent, rec.Code, authorization)
	}

	for _, path := range []string{
		"/api/v1/repository-owners",
		"/api/v3/user",
		"/api/v3/repos/pingcap/agentteam/collaborators/alice",
	} {
		called = false
		req := httptest.NewRequest(http.MethodGet, path, nil)
		req.Header.Set("Authorization", "Bearer ags_rat_example")
		rec := httptest.NewRecorder()
		handler.ServeHTTP(rec, req)
		require.False(t, called, path)
		require.Equal(t, http.StatusUnauthorized, rec.Code, path)
	}
}

func TestAGSExtensionAPIUsesSameSessionProtectionAsV3(t *testing.T) {
	called := false
	handler := Register(
		config.ResourceRuntimeConfig{Auth: config.ResourceRuntimeAuthConfig{Enabled: true, Header: "Authorization"}},
		http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
			called = true
			w.WriteHeader(http.StatusCreated)
		}),
		nil, nil, nil, nil, nil, nil, nil, nil,
	)

	req := httptest.NewRequest(http.MethodPost, "/api/ext/v1/user/orgs", nil)
	req.Header.Set("Authorization", "Bearer "+routerTestJWT(map[string]any{"sub": "usr_1"}))
	rec := httptest.NewRecorder()
	handler.ServeHTTP(rec, req)
	require.True(t, called)
	require.Equal(t, http.StatusCreated, rec.Code)

	called = false
	req = httptest.NewRequest(http.MethodPost, "/api/ext/v1/user/orgs", nil)
	rec = httptest.NewRecorder()
	handler.ServeHTTP(rec, req)
	require.False(t, called)
	require.Equal(t, http.StatusUnauthorized, rec.Code)
}

func TestAGSGlobalRoutesPreservePublicAndOAuthContracts(t *testing.T) {
	t.Parallel()
	called := 0
	handler := Register(
		config.ResourceRuntimeConfig{Auth: config.ResourceRuntimeAuthConfig{Enabled: true, Header: "X-AgentTeam-Verified-JWT"}},
		http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
			called++
			w.WriteHeader(http.StatusNoContent)
		}),
		nil, nil, nil, nil, nil, nil, nil, nil,
	)

	for _, route := range []struct {
		method string
		path   string
	}{
		{method: http.MethodGet, path: "/api/ext/v1/openapi.json"},
		{method: http.MethodPost, path: "/api/ext/v1/agents"},
		{method: http.MethodPost, path: "/login/device/code"},
		{method: http.MethodGet, path: "/auth/connected/login"},
	} {
		recorder := httptest.NewRecorder()
		handler.ServeHTTP(recorder, httptest.NewRequest(route.method, route.path, nil))
		require.Equal(t, http.StatusNoContent, recorder.Code, route.path)
	}
	require.Equal(t, 4, called)
}

func TestGenericAGSCredentialBypassesTrustedAgentTeamJWTMiddleware(t *testing.T) {
	called := false
	handler := Register(
		config.ResourceRuntimeConfig{Auth: config.ResourceRuntimeAuthConfig{Enabled: true, Header: "X-AgentTeam-Verified-JWT"}},
		http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
			called = true
			w.WriteHeader(http.StatusNoContent)
		}),
		nil, nil, nil, nil, nil, nil, nil, nil,
	)

	for _, path := range []string{"/api/ext/v1/user", "/api/graphql"} {
		called = false
		req := httptest.NewRequest(http.MethodGet, path, nil)
		req.Header.Set("Authorization", "Bearer ordinary-ags-token")
		rec := httptest.NewRecorder()
		handler.ServeHTTP(rec, req)
		require.True(t, called, path)
		require.Equal(t, http.StatusNoContent, rec.Code, path)
	}
}

func TestRepositoryNameAvailabilityReturnsPrimaryMemoryPreview(t *testing.T) {
	repoStore := newRouterRepositoryStore(t)
	handler := Register(
		config.ResourceRuntimeConfig{Auth: config.ResourceRuntimeAuthConfig{Enabled: true, Header: "Authorization"}},
		http.NotFoundHandler(),
		nil,
		nil,
		nil,
		nil,
		nil,
		nil,
		nil,
		nil,
		WithRepositoryNameAvailability(repoStore, nil),
	)

	req := httptest.NewRequest(http.MethodGet, "/api/v1/repository-names/available?resourceType=memory&name=Runbooks&storageScope=personal", nil)
	req.Header.Set("Authorization", "Bearer "+routerTestJWT(map[string]any{"sub": "usr_1"}))
	req.Header.Set(meshxapi.HeaderWorkspaceID, "wsp_1")
	rec := httptest.NewRecorder()

	handler.ServeHTTP(rec, req)

	require.Equal(t, http.StatusOK, rec.Code, rec.Body.String())
	require.JSONEq(t, `{"repoName":"runbooks.mem","repoFullName":"usr_1/runbooks.mem","storageOwnerLogin":"usr_1","available":true}`, rec.Body.String())
}

func TestRepositoryNameAvailabilityReturnsConflictAndSuggestedName(t *testing.T) {
	repoStore := newRouterRepositoryStore(t)
	now := time.Date(2026, 6, 19, 12, 0, 0, 0, time.UTC)
	_, err := repoStore.Upsert(context.Background(), reposstore.ResourceRepository{
		ID:           "rr_existing",
		WorkspaceID:  "wsp_1",
		ResourceType: reposstore.ResourceTypeMemory,
		ResourceID:   "mem_existing",
		RepoRole:     reposstore.RepoRolePrimary,
		OwnerKind:    repos.RepositoryStorageScopePersonal,
		RepoOwner:    "usr_1",
		RepoName:     "runbooks.mem",
		RepoFullName: "usr_1/runbooks.mem",
		CreatedBy:    "usr_1",
		UpdatedBy:    "usr_1",
		CreatedAt:    now,
		UpdatedAt:    now,
	})
	require.NoError(t, err)
	handler := Register(
		config.ResourceRuntimeConfig{Auth: config.ResourceRuntimeAuthConfig{Enabled: true, Header: "Authorization"}},
		http.NotFoundHandler(),
		nil,
		nil,
		nil,
		nil,
		nil,
		nil,
		nil,
		nil,
		WithRepositoryNameAvailability(repoStore, nil),
	)

	req := httptest.NewRequest(http.MethodGet, "/api/v1/repository-names/available?resourceType=memory&name=Runbooks&storageScope=personal", nil)
	req.Header.Set("Authorization", "Bearer "+routerTestJWT(map[string]any{"sub": "usr_1"}))
	req.Header.Set(meshxapi.HeaderWorkspaceID, "wsp_1")
	rec := httptest.NewRecorder()

	handler.ServeHTTP(rec, req)

	require.Equal(t, http.StatusOK, rec.Code, rec.Body.String())
	var body struct {
		RepoName          string `json:"repoName"`
		RepoFullName      string `json:"repoFullName"`
		StorageOwnerLogin string `json:"storageOwnerLogin"`
		Available         bool   `json:"available"`
		SuggestedRepoName string `json:"suggestedRepoName"`
		Conflict          struct {
			Field             string `json:"field"`
			SuggestedRepoName string `json:"suggestedRepoName"`
			RepoName          string `json:"repoName"`
			RepoFullName      string `json:"repoFullName"`
		} `json:"conflict"`
	}
	require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &body))
	require.Equal(t, "runbooks.mem", body.RepoName)
	require.Equal(t, "usr_1/runbooks.mem", body.RepoFullName)
	require.Equal(t, "usr_1", body.StorageOwnerLogin)
	require.False(t, body.Available)
	require.Equal(t, "runbooks-1", body.SuggestedRepoName)
	require.Equal(t, repos.RepositoryNameFieldName, body.Conflict.Field)
	require.Equal(t, "runbooks-1", body.Conflict.SuggestedRepoName)
	require.Equal(t, "runbooks.mem", body.Conflict.RepoName)
	require.Equal(t, "usr_1/runbooks.mem", body.Conflict.RepoFullName)
}

func TestRepositoryOwnerErrorHandlingMapsResourceRepoNameConflictDetails(t *testing.T) {
	repoStore := newRouterRepositoryStore(t)
	now := time.Date(2026, 6, 19, 12, 0, 0, 0, time.UTC)
	_, err := repoStore.Upsert(context.Background(), reposstore.ResourceRepository{
		ID:           "rr_existing",
		WorkspaceID:  "wsp_1",
		ResourceType: reposstore.ResourceTypeMemory,
		ResourceID:   "mem_existing",
		RepoRole:     reposstore.RepoRolePrimary,
		OwnerKind:    repos.RepositoryStorageScopePersonal,
		RepoOwner:    "usr_1",
		RepoName:     "runbooks.mem",
		RepoFullName: "usr_1/runbooks.mem",
		CreatedBy:    "usr_1",
		UpdatedBy:    "usr_1",
		CreatedAt:    now,
		UpdatedAt:    now,
	})
	require.NoError(t, err)
	handler := trusted.Middleware(trusted.Options{Enabled: true, Header: "Authorization"})(meshxapi.HandleAuth(func(request meshxapi.AuthenticatedRequest) (meshxapi.Response, error) {
		return meshxapi.Response{}, repos.EnsureRepositoryFullNameAvailableForField(
			request.Context(),
			repoStore,
			"usr_1/runbooks.mem",
			reposstore.ResourceTypeKBSource,
			"src_1",
			reposstore.RepoRolePrimary,
			repos.RepositoryNameFieldRepoNameOverride,
			"runbooks",
		)
	}, repositoryOwnerErrorHandling()))

	req := httptest.NewRequest(http.MethodGet, "/test", nil)
	req.Header.Set("Authorization", "Bearer "+routerTestJWT(map[string]any{"sub": "usr_1"}))
	req.Header.Set(meshxapi.HeaderWorkspaceID, "wsp_1")
	rec := httptest.NewRecorder()

	handler.ServeHTTP(rec, req)

	require.Equal(t, http.StatusConflict, rec.Code, rec.Body.String())
	var body struct {
		Error   string `json:"error"`
		Details struct {
			Field             string `json:"field"`
			SuggestedRepoName string `json:"suggestedRepoName"`
			ResourceType      string `json:"resourceType"`
			ResourceID        string `json:"resourceId"`
			RepoRole          string `json:"repoRole"`
			RepoName          string `json:"repoName"`
			RepoFullName      string `json:"repoFullName"`
		} `json:"details"`
	}
	require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &body))
	require.Equal(t, repos.RepositoryNameConflictCode, body.Error)
	require.Equal(t, repos.RepositoryNameFieldRepoNameOverride, body.Details.Field)
	require.Equal(t, "runbooks", body.Details.SuggestedRepoName)
	require.Equal(t, reposstore.ResourceTypeMemory, body.Details.ResourceType)
	require.Equal(t, "mem_existing", body.Details.ResourceID)
	require.Equal(t, reposstore.RepoRolePrimary, body.Details.RepoRole)
	require.Equal(t, "runbooks.mem", body.Details.RepoName)
	require.Equal(t, "usr_1/runbooks.mem", body.Details.RepoFullName)
}

func TestRepositoryNameAvailabilityRejectsReservedOverrideWithFieldDetails(t *testing.T) {
	repoStore := newRouterRepositoryStore(t)
	handler := Register(
		config.ResourceRuntimeConfig{Auth: config.ResourceRuntimeAuthConfig{Enabled: true, Header: "Authorization"}},
		http.NotFoundHandler(),
		nil,
		nil,
		nil,
		nil,
		nil,
		nil,
		nil,
		nil,
		WithRepositoryNameAvailability(repoStore, nil),
	)

	req := httptest.NewRequest(http.MethodGet, "/api/v1/repository-names/available?resourceType=memory&name=Runbooks&repoNameOverride=runbooks.mem&storageScope=personal", nil)
	req.Header.Set("Authorization", "Bearer "+routerTestJWT(map[string]any{"sub": "usr_1"}))
	req.Header.Set(meshxapi.HeaderWorkspaceID, "wsp_1")
	rec := httptest.NewRecorder()

	handler.ServeHTTP(rec, req)

	require.Equal(t, http.StatusConflict, rec.Code, rec.Body.String())
	var body struct {
		Error   string `json:"error"`
		Details struct {
			Field             string `json:"field"`
			SuggestedRepoName string `json:"suggestedRepoName"`
		} `json:"details"`
	}
	require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &body))
	require.Equal(t, repos.RepositoryReservedSuffixCode, body.Error)
	require.Equal(t, repos.RepositoryNameFieldRepoNameOverride, body.Details.Field)
	require.Equal(t, "runbooks", body.Details.SuggestedRepoName)
}

func TestRepositoryNameAvailabilityInheritsKnowledgeBaseStorageForSource(t *testing.T) {
	repoStore := newRouterRepositoryStore(t)
	handler := Register(
		config.ResourceRuntimeConfig{Auth: config.ResourceRuntimeAuthConfig{Enabled: true, Header: "Authorization"}},
		http.NotFoundHandler(),
		nil,
		nil,
		nil,
		nil,
		nil,
		nil,
		nil,
		nil,
		WithRepositoryNameAvailability(repoStore, fakeKnowledgeBaseStorageResolver{base: kb.KnowledgeBase{ID: "kb_1", WorkspaceID: "wsp_1", StorageScope: repos.RepositoryStorageScopeWorkspace, StorageOwnerLogin: "wsp_1"}}),
	)

	req := httptest.NewRequest(http.MethodGet, "/api/v1/repository-names/available?resourceType=kb_source&knowledgeBaseId=kb_1&name=Runbooks&storageScope=personal", nil)
	req.Header.Set("Authorization", "Bearer "+routerTestJWT(map[string]any{"sub": "usr_1"}))
	req.Header.Set(meshxapi.HeaderWorkspaceID, "wsp_1")
	rec := httptest.NewRecorder()

	handler.ServeHTTP(rec, req)

	require.Equal(t, http.StatusOK, rec.Code, rec.Body.String())
	require.JSONEq(t, `{"repoName":"runbooks","repoFullName":"wsp_1/runbooks","storageOwnerLogin":"wsp_1","available":true}`, rec.Body.String())
}

type fakeKnowledgeBaseStorageResolver struct {
	base kb.KnowledgeBase
}

func (r fakeKnowledgeBaseStorageResolver) GetKnowledgeBase(_ context.Context, workspaceID, _ string, id string) (kb.KnowledgeBase, error) {
	if r.base.ID == id && r.base.WorkspaceID == workspaceID {
		return r.base, nil
	}
	return kb.KnowledgeBase{}, kbservice.ErrNotFound
}

func newRouterRepositoryStore(t *testing.T) *reposstore.Store {
	t.Helper()
	db, err := gorm.Open(sqlite.Open("file:"+t.Name()+"?mode=memory&cache=shared"), &gorm.Config{})
	require.NoError(t, err)
	require.NoError(t, db.AutoMigrate(&reposstore.ResourceRepository{}, &reposstore.ResourceRepositoryAccessGrant{}))
	return reposstore.New(db)
}
