package roles

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

	"github.com/stretchr/testify/require"
	"gorm.io/driver/sqlite"
	"gorm.io/gorm"

	"github.com/tidbcloud/AgentTeam/internal/api/meshxapi"
	"github.com/tidbcloud/AgentTeam/internal/identity/trusted"
	"github.com/tidbcloud/AgentTeam/internal/service/resource/repos"
	roleservice "github.com/tidbcloud/AgentTeam/internal/service/roles"
	rolesstore "github.com/tidbcloud/AgentTeam/internal/store/resource/roles"
)

type recordingOperationService struct {
	call     string
	calls    int
	err      error
	role     roleservice.Role
	recovery roleservice.RoleOperationRecovery
}

func (service *recordingOperationService) PreflightCreateRoleOperation(context.Context, string, string, roleservice.Mutation) error {
	service.calls++
	service.call = "preflight-create"
	return service.err
}
func (service *recordingOperationService) PreflightUpdateRoleOperation(context.Context, string, string, string, string, roleservice.RoleUpdatePatch) error {
	service.calls++
	service.call = "preflight-update"
	return service.err
}
func (service *recordingOperationService) PreflightDeleteRoleOperation(context.Context, string, string, string, string) error {
	service.calls++
	service.call = "preflight-delete"
	return service.err
}
func (service *recordingOperationService) PreflightApplyRoleOperation(ctx context.Context, workspaceID, subject, _ string, request roleservice.RealizationRequest) (roleservice.RealizationImpact, error) {
	service.calls++
	if err := (roleservice.ClaimsAgentOwnershipSource{}).RequireOwnedAgent(ctx, workspaceID, subject, request.AgentID); err != nil {
		return roleservice.RealizationImpact{}, err
	}
	service.call = "preflight-apply"
	return roleservice.RealizationImpact{}, service.err
}
func (service *recordingOperationService) CreateRoleOperation(context.Context, string, string, roleservice.MutationOperation, roleservice.Mutation) (roleservice.Role, error) {
	service.calls++
	service.call = "execute-create"
	return service.role, service.err
}
func (service *recordingOperationService) UpdateRoleOperation(context.Context, string, string, string, roleservice.MutationOperation, string, roleservice.RoleUpdatePatch) (roleservice.Role, error) {
	service.calls++
	service.call = "execute-update"
	return service.role, service.err
}
func (service *recordingOperationService) DeleteRoleOperation(context.Context, string, string, string, roleservice.MutationOperation, string) (roleservice.DeleteRoleResult, error) {
	service.calls++
	service.call = "execute-delete"
	return roleservice.DeleteRoleResult{RoleID: service.role.ID, RoleVersionID: service.role.RoleVersionID}, service.err
}
func (service *recordingOperationService) ApplyRoleOperation(ctx context.Context, workspaceID, subject, _ string, _ roleservice.MutationOperation, request roleservice.RealizationRequest) (roleservice.RealizationResult, error) {
	service.calls++
	if err := (roleservice.ClaimsAgentOwnershipSource{}).RequireOwnedAgent(ctx, workspaceID, subject, request.AgentID); err != nil {
		return roleservice.RealizationResult{}, err
	}
	service.call = "execute-apply"
	return roleservice.RealizationResult{}, service.err
}
func (service *recordingOperationService) RecoverRoleOperation(
	_ context.Context,
	_, action string,
	_ roleservice.MutationOperation,
) (roleservice.RoleOperationRecovery, error) {
	service.calls++
	service.call = "recover-" + action
	return service.recovery, service.err
}

func TestRoleOperationTransportPreflightIsStrictAndNoContent(t *testing.T) {
	service := &recordingOperationService{}
	recorder := serveRoleOperation(t, service, RoleOperationPreflightPath, `{
		"create":{"name":"Reviewer","definition":"Review changes"}
	}`, validRoleOperationIdentity())
	require.Equal(t, http.StatusNoContent, recorder.Code)
	require.Empty(t, recorder.Body.String())
	require.Equal(t, "preflight-create", service.call)

	for name, body := range map[string]string{
		"unknown":          `{"create":{"name":"n","definition":"d"},"workspaceId":"wsp_other"}`,
		"trailing":         `{"create":{"name":"n","definition":"d"}} {}`,
		"empty union":      `{}`,
		"multiple actions": `{"create":{"name":"n","definition":"d"},"delete":{"roleId":"role_1","expectedRoleVersionId":"rlv_1"}}`,
		"malformed id":     `{"delete":{"roleId":"../role","expectedRoleVersionId":"rlv_1"}}`,
	} {
		t.Run(name, func(t *testing.T) {
			service.call = ""
			response := serveRoleOperation(t, service, RoleOperationPreflightPath, body, validRoleOperationIdentity())
			require.Equal(t, http.StatusBadRequest, response.Code)
			requireWireError(t, response, "invalid_request")
			require.Empty(t, service.call)
		})
	}
}

func TestRoleOperationTransportRequiresProtectedClaimsBoundary(t *testing.T) {
	body := `{"apply":{"roleId":"role_1","roleVersionId":"rlv_1","agentId":"agt_1"}}`
	for name, mutate := range map[string]func(*http.Request){
		"no identity": func(request *http.Request) {
			*request = *request.WithContext(context.Background())
		},
		"empty subject": func(request *http.Request) {
			*request = *request.WithContext(trusted.WithIdentity(request.Context(), trusted.Identity{RawClaims: validRoleOperationIdentity().RawClaims}))
		},
		"no purpose": func(request *http.Request) {
			identity := validRoleOperationIdentity()
			delete(identity.RawClaims, "agentteam_internal_purpose")
			*request = *request.WithContext(trusted.WithIdentity(request.Context(), identity))
		},
		"browser purpose": func(request *http.Request) {
			identity := validRoleOperationIdentity()
			identity.RawClaims["agentteam_internal_purpose"] = "browser"
			*request = *request.WithContext(trusted.WithIdentity(request.Context(), identity))
		},
		"no workspace": func(request *http.Request) {
			request.Header.Del(meshxapi.HeaderWorkspaceID)
		},
		"wrong ownership workspace": func(request *http.Request) {
			identity := validRoleOperationIdentity()
			identity.RawClaims["agent_ownership_workspace_id"] = "wsp_other"
			*request = *request.WithContext(trusted.WithIdentity(request.Context(), identity))
		},
		"target not owned": func(request *http.Request) {
			identity := validRoleOperationIdentity()
			identity.RawClaims["owned_agent_ids"] = []any{"agt_other"}
			*request = *request.WithContext(trusted.WithIdentity(request.Context(), identity))
		},
	} {
		t.Run(name, func(t *testing.T) {
			service := &recordingOperationService{}
			request := newRoleOperationRequest(RoleOperationPreflightPath, body, validRoleOperationIdentity())
			if name == "no identity" {
				request = request.WithContext(context.Background())
			} else {
				mutate(request)
			}
			recorder := httptest.NewRecorder()
			NewRoleOperationTransportHandler(service).ServeHTTP(recorder, request)
			require.Equal(t, http.StatusForbidden, recorder.Code)
			requireWireError(t, recorder, "authority_denied")
			require.Empty(t, service.call)
		})
	}
}

func TestRoleOperationTransportChecksProtectedBoundaryBeforeParsingBody(t *testing.T) {
	service := &recordingOperationService{}
	identity := validRoleOperationIdentity()
	delete(identity.RawClaims, "agentteam_internal_purpose")
	request := newRoleOperationRequest(RoleOperationPreflightPath, `{`, identity)
	recorder := httptest.NewRecorder()

	NewRoleOperationTransportHandler(service).ServeHTTP(recorder, request)

	require.Equal(t, http.StatusForbidden, recorder.Code)
	requireWireError(t, recorder, "authority_denied")
	require.Empty(t, service.call)
}

func TestRoleOperationTransportRejectsWorkspaceClaimMismatchBeforeBodyAndService(t *testing.T) {
	preflightBodies := map[string]string{
		"create": `{"create":{"name":"Reviewer","definition":"Review changes"}}`,
		"update": `{"update":{"roleId":"role_1","expectedRoleVersionId":"rlv_1","description":"updated"}}`,
		"delete": `{"delete":{"roleId":"role_1","expectedRoleVersionId":"rlv_1"}}`,
		"apply":  `{"apply":{"roleId":"role_1","roleVersionId":"rlv_1","agentId":"agt_1"}}`,
	}
	const executePrefix = `{"operationKey":"op_1","requestDigest":"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",`
	for action, preflightBody := range preflightBodies {
		t.Run(action, func(t *testing.T) {
			for _, testCase := range []struct {
				name string
				path string
				body string
			}{
				{name: "preflight", path: RoleOperationPreflightPath, body: preflightBody},
				{name: "execute", path: RoleOperationExecutePath, body: executePrefix + strings.TrimPrefix(preflightBody, "{")},
				{name: "recover", path: RoleOperationRecoveryPath, body: executePrefix + strings.TrimPrefix(preflightBody, "{")},
			} {
				t.Run(testCase.name, func(t *testing.T) {
					service := &recordingOperationService{
						role: roleservice.Role{ID: "role_authority", RoleVersionID: "rlv_authority"},
					}
					identity := validRoleOperationIdentity()
					identity.RawClaims["agent_ownership_workspace_id"] = "wsp_claim"
					request := newRoleOperationRequest(testCase.path, testCase.body, identity)
					request.Header.Set(meshxapi.HeaderWorkspaceID, "wsp_header")
					recorder := httptest.NewRecorder()

					NewRoleOperationTransportHandler(service).ServeHTTP(recorder, request)

					require.Equal(t, http.StatusForbidden, recorder.Code)
					requireWireError(t, recorder, "authority_denied")
					require.Zero(t, service.calls)
				})
			}
		})
	}

	service := &recordingOperationService{}
	identity := validRoleOperationIdentity()
	identity.RawClaims["agent_ownership_workspace_id"] = "wsp_claim"
	request := newRoleOperationRequest(RoleOperationPreflightPath, `{`, identity)
	request.Header.Set(meshxapi.HeaderWorkspaceID, "wsp_header")
	recorder := httptest.NewRecorder()

	NewRoleOperationTransportHandler(service).ServeHTTP(recorder, request)

	require.Equal(t, http.StatusForbidden, recorder.Code)
	requireWireError(t, recorder, "authority_denied")
	require.Zero(t, service.calls)
}

func TestRoleOperationTransportRejectsDuplicateJSONKeys(t *testing.T) {
	for name, bodies := range map[string][2]string{
		"root action": {
			`{"create":{"name":"Reviewer","definition":"Review changes"},"create":{"name":"Release","definition":"Release changes"}}`,
			`{"operationKey":"op_1","requestDigest":"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","create":{"name":"Reviewer","definition":"Review changes"},"create":{"name":"Release","definition":"Release changes"}}`,
		},
		"nested field": {
			`{"create":{"name":"Reviewer","name":"Release","definition":"Review changes"}}`,
			`{"operationKey":"op_1","requestDigest":"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","create":{"name":"Reviewer","name":"Release","definition":"Review changes"}}`,
		},
		"case variant root": {
			`{"create":{"name":"Reviewer","definition":"Review changes"},"Create":{"name":"Release","definition":"Release changes"}}`,
			`{"operationKey":"op_1","requestDigest":"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","create":{"name":"Reviewer","definition":"Review changes"},"Create":{"name":"Release","definition":"Release changes"}}`,
		},
		"case variant nested": {
			`{"create":{"name":"Reviewer","Name":"Release","definition":"Review changes"}}`,
			`{"operationKey":"op_1","requestDigest":"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","create":{"name":"Reviewer","Name":"Release","definition":"Review changes"}}`,
		},
		"unicode fold root": {
			`{"operationKey":"op_other","operationKey":"op_1","create":{"name":"Reviewer","definition":"Review changes"}}`,
			`{"operationKey":"op_other","operationKey":"op_1","requestDigest":"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","create":{"name":"Reviewer","definition":"Review changes"}}`,
		},
		"unicode fold nested": {
			`{"update":{"roleId":"role_1","expectedRoleVersionId":"rlv_1","description":"first","deſcription":"last"}}`,
			`{"operationKey":"op_1","requestDigest":"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","update":{"roleId":"role_1","expectedRoleVersionId":"rlv_1","description":"first","deſcription":"last"}}`,
		},
	} {
		t.Run(name, func(t *testing.T) {
			for index, path := range []string{
				RoleOperationPreflightPath,
				RoleOperationExecutePath,
				RoleOperationRecoveryPath,
			} {
				t.Run(path, func(t *testing.T) {
					service := &recordingOperationService{}
					bodyIndex := index
					if bodyIndex == 2 {
						bodyIndex = 1
					}
					response := serveRoleOperation(
						t, service, path, bodies[bodyIndex], validRoleOperationIdentity(),
					)
					require.Equal(t, http.StatusBadRequest, response.Code)
					requireWireError(t, response, "invalid_request")
					require.Zero(t, service.calls)
				})
			}
		})
	}
}

func TestRoleOperationTransportApplyRequiresOwnedTarget(t *testing.T) {
	for _, path := range []string{
		RoleOperationPreflightPath,
		RoleOperationExecutePath,
		RoleOperationRecoveryPath,
	} {
		t.Run(path, func(t *testing.T) {
			service := &recordingOperationService{}
			body := `{"apply":{"roleId":"role_1","roleVersionId":"rlv_1","agentId":"agt_1"}}`
			if path != RoleOperationPreflightPath {
				body = `{"operationKey":"op_1","requestDigest":"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","apply":{"roleId":"role_1","roleVersionId":"rlv_1","agentId":"agt_1"}}`
			}
			identity := validRoleOperationIdentity()
			delete(identity.RawClaims, "owned_agent_ids")
			response := serveRoleOperation(t, service, path, body, identity)
			if path == RoleOperationRecoveryPath {
				require.Equal(t, http.StatusOK, response.Code)
				require.JSONEq(t, `{"found":false}`, response.Body.String())
				require.Equal(t, 1, service.calls)
				require.Equal(t, "recover-apply", service.call)
				return
			}
			require.Equal(t, http.StatusForbidden, response.Code)
			requireWireError(t, response, "authority_denied")
			if path == RoleOperationPreflightPath {
				require.Zero(t, service.calls)
			} else {
				require.Equal(t, 1, service.calls,
					"fresh execute must reach receipt-first service admission before ownership rejection")
			}
			require.Empty(t, service.call)
		})
	}
}

func TestRoleOperationTransportRecoveryReturnsStrictFoundMissResult(t *testing.T) {
	body := `{
		"operationKey":"op_1",
		"requestDigest":"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
		"create":{"name":"Reviewer","definition":"Review changes"}
	}`
	service := &recordingOperationService{}
	response := serveRoleOperation(
		t, service, RoleOperationRecoveryPath, body, validRoleOperationIdentity(),
	)
	require.Equal(t, http.StatusOK, response.Code)
	require.JSONEq(t, `{"found":false}`, response.Body.String())
	require.Equal(t, "recover-create", service.call)

	service.recovery = roleservice.RoleOperationRecovery{
		Found: true, RoleID: "role_1", RoleVersionID: "rlv_1",
	}
	response = serveRoleOperation(
		t, service, RoleOperationRecoveryPath, body, validRoleOperationIdentity(),
	)
	require.Equal(t, http.StatusOK, response.Code)
	require.JSONEq(t, `{
		"found":true,
		"roleId":"role_1",
		"roleVersionId":"rlv_1"
	}`, response.Body.String())

	service.err = roleservice.ErrRoleMutationConflict
	response = serveRoleOperation(
		t, service, RoleOperationRecoveryPath, body, validRoleOperationIdentity(),
	)
	require.Equal(t, http.StatusConflict, response.Code)
	requireWireError(t, response, "role_operation_conflict")
}

func TestRoleOperationTransportExecuteReplaysCompletedApplyAfterClaimsOwnershipDrift(t *testing.T) {
	database, err := gorm.Open(
		sqlite.Open("file:role_operation_transport_receipt_replay?mode=memory&cache=shared"),
		&gorm.Config{},
	)
	require.NoError(t, err)
	require.NoError(t, database.AutoMigrate(&rolesstore.RoleOperationReceipt{}))
	const digest = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
	require.NoError(t, database.Create(&rolesstore.RoleOperationReceipt{
		WorkspaceID: "wsp_1", OperationKey: "op_replay", Action: "apply",
		RequestDigest: digest,
		ResultJSON: `{
			"realizationId":"rrl_1",
			"roleId":"role_1",
			"roleVersionId":"rlv_1",
			"agentId":"agt_1"
		}`,
		CreatedAt: time.Now().UTC(),
	}).Error)
	service := roleservice.New(rolesstore.New(database), roleservice.Options{})
	identity := validRoleOperationIdentity()
	delete(identity.RawClaims, "owned_agent_ids")

	response := serveRoleOperation(t, service, RoleOperationExecutePath, `{
		"operationKey":"op_replay",
		"requestDigest":"`+digest+`",
		"apply":{"roleId":"role_1","roleVersionId":"rlv_1","agentId":"agt_1"}
	}`, identity)

	require.Equal(t, http.StatusOK, response.Code, response.Body.String())
	require.JSONEq(t, `{"roleId":"role_1","roleVersionId":"rlv_1"}`, response.Body.String())
}

func TestRoleOperationTransportNonApplyAllowsEmptyOwnedAgentList(t *testing.T) {
	for action, bodies := range map[string][2]string{
		"create": {
			`{"create":{"name":"Reviewer","definition":"Review changes"}}`,
			`{"operationKey":"op_create","requestDigest":"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","create":{"name":"Reviewer","definition":"Review changes"}}`,
		},
		"update": {
			`{"update":{"roleId":"role_1","expectedRoleVersionId":"rlv_1","description":"updated"}}`,
			`{"operationKey":"op_update","requestDigest":"bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb","update":{"roleId":"role_1","expectedRoleVersionId":"rlv_1","description":"updated"}}`,
		},
		"delete": {
			`{"delete":{"roleId":"role_1","expectedRoleVersionId":"rlv_1"}}`,
			`{"operationKey":"op_delete","requestDigest":"cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc","delete":{"roleId":"role_1","expectedRoleVersionId":"rlv_1"}}`,
		},
	} {
		t.Run(action, func(t *testing.T) {
			for index, path := range []string{RoleOperationPreflightPath, RoleOperationExecutePath} {
				t.Run(path, func(t *testing.T) {
					service := &recordingOperationService{
						role: roleservice.Role{ID: "role_authority", RoleVersionID: "rlv_authority"},
					}
					identity := validRoleOperationIdentity()
					delete(identity.RawClaims, "owned_agent_ids")
					response := serveRoleOperation(t, service, path, bodies[index], identity)
					if path == RoleOperationPreflightPath {
						require.Equal(t, http.StatusNoContent, response.Code)
					} else {
						require.Equal(t, http.StatusOK, response.Code)
					}
					require.Equal(t, 1, service.calls)
				})
			}
		})
	}
}

func TestRoleOperationTransportKeepsRequestBodyLimit(t *testing.T) {
	service := &recordingOperationService{}
	body := `{"create":{"name":"Reviewer","definition":"` +
		strings.Repeat("x", maxRoleOperationBodyBytes) + `"}}`
	response := serveRoleOperation(
		t, service, RoleOperationPreflightPath, body, validRoleOperationIdentity(),
	)
	require.Equal(t, http.StatusBadRequest, response.Code)
	requireWireError(t, response, "invalid_request")
	require.Zero(t, service.calls)
}

func TestRoleOperationTransportExecuteReturnsAuthorityRoleIDs(t *testing.T) {
	service := &recordingOperationService{role: roleservice.Role{ID: "role_authority", RoleVersionID: "rlv_authority"}}
	recorder := serveRoleOperation(t, service, RoleOperationExecutePath, `{
		"operationKey":"op_1",
		"requestDigest":"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
		"create":{"name":"Reviewer","definition":"Review changes"}
	}`, validRoleOperationIdentity())
	require.Equal(t, http.StatusOK, recorder.Code)
	require.JSONEq(t, `{"roleId":"role_authority","roleVersionId":"rlv_authority"}`, recorder.Body.String())
	require.Equal(t, "execute-create", service.call)

	for name, body := range map[string]string{
		"missing operation key":   `{"requestDigest":"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","create":{"name":"n","definition":"d"}}`,
		"uppercase operation key": `{"operationKey":"op_BAD","requestDigest":"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","create":{"name":"n","definition":"d"}}`,
		"malformed digest":        `{"operationKey":"op_1","requestDigest":"not-a-digest","create":{"name":"n","definition":"d"}}`,
	} {
		t.Run(name, func(t *testing.T) {
			service.call = ""
			response := serveRoleOperation(t, service, RoleOperationExecutePath, body, validRoleOperationIdentity())
			require.Equal(t, http.StatusBadRequest, response.Code)
			requireWireError(t, response, "invalid_request")
			require.Empty(t, service.call)
		})
	}

	service.role = roleservice.Role{}
	recorder = serveRoleOperation(t, service, RoleOperationExecutePath, `{
		"operationKey":"op_2",
		"requestDigest":"bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
		"create":{"name":"Reviewer","definition":"Review changes"}
	}`, validRoleOperationIdentity())
	require.Equal(t, http.StatusInternalServerError, recorder.Code)
	requireWireError(t, recorder, "internal_error")
}

func TestRoleOperationTransportStableErrorMapping(t *testing.T) {
	for _, testCase := range []struct {
		name   string
		err    error
		status int
		code   string
	}{
		{"invalid", repos.ErrInvalidArgument, http.StatusBadRequest, "invalid_request"},
		{"denied", repos.ErrForbidden, http.StatusForbidden, "authority_denied"},
		{"missing", repos.ErrNotFound, http.StatusNotFound, "not_found"},
		{"version", roleservice.ErrRoleVersionConflict, http.StatusConflict, "role_version_conflict"},
		{"operation", roleservice.ErrRoleMutationConflict, http.StatusConflict, "role_operation_conflict"},
		{"unknown", errors.New("boom"), http.StatusInternalServerError, "internal_error"},
	} {
		t.Run(testCase.name, func(t *testing.T) {
			service := &recordingOperationService{err: testCase.err}
			response := serveRoleOperation(t, service, RoleOperationPreflightPath, `{"create":{"name":"n","definition":"d"}}`, validRoleOperationIdentity())
			require.Equal(t, testCase.status, response.Code)
			requireWireError(t, response, testCase.code)
		})
	}
}

func validRoleOperationIdentity() trusted.Identity {
	return trusted.Identity{
		Subject: "usr_1",
		RawClaims: map[string]any{
			"agentteam_internal_purpose":        RoleOperationInternalPurpose,
			"agent_ownership_workspace_id": "wsp_1",
			"owned_agent_ids":              []any{"agt_1"},
		},
	}
}

func serveRoleOperation(t *testing.T, service OperationService, path, body string, identity trusted.Identity) *httptest.ResponseRecorder {
	t.Helper()
	request := newRoleOperationRequest(path, body, identity)
	recorder := httptest.NewRecorder()
	NewRoleOperationTransportHandler(service).ServeHTTP(recorder, request)
	return recorder
}

func newRoleOperationRequest(path, body string, identity trusted.Identity) *http.Request {
	request := httptest.NewRequest(http.MethodPost, path, bytes.NewBufferString(body))
	request.Header.Set(meshxapi.HeaderWorkspaceID, "wsp_1")
	return request.WithContext(trusted.WithIdentity(request.Context(), identity))
}

func requireWireError(t *testing.T, recorder *httptest.ResponseRecorder, code string) {
	t.Helper()
	var body struct {
		Error   string `json:"error"`
		Message string `json:"message"`
	}
	require.NoError(t, json.NewDecoder(strings.NewReader(recorder.Body.String())).Decode(&body))
	require.Equal(t, code, body.Error)
	require.NotEmpty(t, body.Message)
}
