package prwatch

import (
	"context"
	"encoding/json"
	"errors"
	"testing"
	"time"

	"github.com/stretchr/testify/require"

	"github.com/tidbcloud/AgentTeam/internal/service/extensionflow"
	"github.com/tidbcloud/AgentTeam/internal/store/agent"
	"github.com/tidbcloud/AgentTeam/internal/store/app"
	"github.com/tidbcloud/AgentTeam/internal/store/storeerr"
)

type authorizationCall struct {
	workspaceID    string
	agentID        string
	installationID string
	requiredScopes []string
}

type targetCall struct {
	agent  agent.AuthenticatedAgent
	target string
}

type getCall struct {
	workspaceID    string
	actor          app.ServiceEventActor
	provider       string
	installationID string
	watchID        string
	requiredScopes []string
}

type listCall struct {
	workspaceID    string
	actor          app.ServiceEventActor
	installationID string
	targetKind     string
	targetRef      string
	status         string
	options        app.ListServiceEventSubscriptionsOptions
}

type lifecycleCall struct {
	workspaceID string
	actor       app.ServiceEventActor
	watchID     string
	reason      string
}

type fakeStore struct {
	authorization  app.AgentAppAuthorizationRecord
	resolvedTarget app.ServiceEventTarget
	createResult   app.ServiceEventSubscriptionResult
	listPage       app.ServiceEventSubscriptionPage
	getResult      app.ServiceEventSubscriptionRecord

	authorizationCalls []authorizationCall
	targetCalls        []targetCall
	createCalls        []app.CreateServiceEventSubscriptionParams
	listCalls          []listCall
	getCalls           []getCall
	pauseCalls         []lifecycleCall
	resumeCalls        []lifecycleCall
	removeCalls        []lifecycleCall
}

func (store *fakeStore) CheckAgentAppAuthorization(
	_ context.Context,
	workspaceID string,
	agentID string,
	installationID string,
	requiredScopes []string,
) (app.AgentAppAuthorizationRecord, error) {
	store.authorizationCalls = append(store.authorizationCalls, authorizationCall{
		workspaceID: workspaceID, agentID: agentID, installationID: installationID,
		requiredScopes: append([]string(nil), requiredScopes...),
	})
	return store.authorization, nil
}

func (store *fakeStore) ResolveServiceEventSubscriptionTargetForAgentTarget(
	_ context.Context,
	agent agent.AuthenticatedAgent,
	target string,
) (app.ServiceEventTarget, error) {
	store.targetCalls = append(store.targetCalls, targetCall{agent: agent, target: target})
	return store.resolvedTarget, nil
}

func (store *fakeStore) CreateServiceEventSubscription(
	_ context.Context,
	_ string,
	params app.CreateServiceEventSubscriptionParams,
) (app.ServiceEventSubscriptionResult, error) {
	store.createCalls = append(store.createCalls, params)
	return store.createResult, nil
}

func (store *fakeStore) ListServiceEventSubscriptionsPage(
	_ context.Context,
	workspaceID string,
	actor app.ServiceEventActor,
	installationID string,
	targetKind string,
	targetRef string,
	status string,
	options app.ListServiceEventSubscriptionsOptions,
) (app.ServiceEventSubscriptionPage, error) {
	store.listCalls = append(store.listCalls, listCall{
		workspaceID: workspaceID, actor: actor, installationID: installationID,
		targetKind: targetKind, targetRef: targetRef, status: status, options: options,
	})
	return store.listPage, nil
}

func (store *fakeStore) ListServiceEventSubscriptions(
	ctx context.Context,
	workspaceID string,
	actor app.ServiceEventActor,
	installationID string,
	targetKind string,
	targetRef string,
	status string,
	options app.ListServiceEventSubscriptionsOptions,
) ([]app.ServiceEventSubscriptionRecord, error) {
	page, err := store.ListServiceEventSubscriptionsPage(
		ctx, workspaceID, actor, installationID, targetKind, targetRef, status, options,
	)
	return page.Subscriptions, err
}

func (store *fakeStore) GetServiceEventSubscriptionForAppInstallation(
	_ context.Context,
	workspaceID string,
	actor app.ServiceEventActor,
	provider string,
	installationID string,
	watchID string,
	requiredScopes []string,
) (app.ServiceEventSubscriptionRecord, error) {
	store.getCalls = append(store.getCalls, getCall{
		workspaceID: workspaceID, actor: actor, provider: provider,
		installationID: installationID, watchID: watchID,
		requiredScopes: append([]string(nil), requiredScopes...),
	})
	return store.getResult, nil
}

func (store *fakeStore) PauseServiceEventSubscription(
	_ context.Context,
	workspaceID string,
	actor app.ServiceEventActor,
	watchID string,
	reason string,
) (app.ServiceEventSubscriptionRecord, error) {
	store.pauseCalls = append(store.pauseCalls, lifecycleCall{
		workspaceID: workspaceID, actor: actor, watchID: watchID, reason: reason,
	})
	return store.getResult, nil
}

func (store *fakeStore) ResumeServiceEventSubscription(
	_ context.Context,
	workspaceID string,
	actor app.ServiceEventActor,
	watchID string,
) (app.ServiceEventSubscriptionRecord, error) {
	store.resumeCalls = append(store.resumeCalls, lifecycleCall{
		workspaceID: workspaceID, actor: actor, watchID: watchID,
	})
	return store.getResult, nil
}

func (store *fakeStore) RemoveServiceEventSubscription(
	_ context.Context,
	workspaceID string,
	actor app.ServiceEventActor,
	watchID string,
) (app.ServiceEventSubscriptionRecord, error) {
	store.removeCalls = append(store.removeCalls, lifecycleCall{
		workspaceID: workspaceID, actor: actor, watchID: watchID,
	})
	return store.getResult, nil
}

func TestInvokeWatchPullRequestBindsExactIdentityTargetAndResource(t *testing.T) {
	createdAt := time.Date(2026, 8, 12, 1, 2, 3, 0, time.UTC)
	store := newAuthorizedFakeStore("github")
	store.resolvedTarget = app.ServiceEventTarget{
		TargetKind: "thread", TargetRef: "scp_thread",
		DeliveryScopeID: "scp_thread", PermissionCheckScopeID: "scp_parent",
	}
	store.createResult = app.ServiceEventSubscriptionResult{
		Subscription: app.ServiceEventSubscriptionRecord{
			ID: "pwt_1", Provider: "github", AppInstallationID: "ins_github",
			ResourceRef: "https://github.com/PingCAP/AgentTeam/pull/42",
			ResourceKey: "github:pingcap/agentteam#42", Status: "active",
			EventFilters: []byte(`{"events":["comments","reviews"]}`),
			CreatedAt:    createdAt, UpdatedAt: createdAt,
		},
	}
	service := New(store)

	data, err := service.Invoke(context.Background(), Invocation{
		Provider: "github", WorkspaceID: "wsp_1", AgentID: "agt_1",
		AgentMemberID: "mem_agent", ServiceInstallationID: "ins_github",
		Action: "watch-pull-request",
		Input: json.RawMessage(`{
			"target":"#agentteam-in-agentteam:31e0897c",
			"pullRequest":"https://github.com/PingCAP/AgentTeam/pull/42",
			"events":[" comments ","","reviews"],
			"notifyPrincipal":false
		}`),
	})
	require.NoError(t, err)
	require.JSONEq(t, `{
		"watchId":"pwt_1",
		"provider":"github",
		"target":"#agentteam-in-agentteam:31e0897c",
		"pullRequest":"https://github.com/PingCAP/AgentTeam/pull/42",
		"resource":"github:pingcap/agentteam#42",
		"status":"active",
		"events":["comments","reviews"],
		"createdAt":"2026-08-12T01:02:03Z",
		"updatedAt":"2026-08-12T01:02:03Z"
	}`, string(data))

	require.Equal(t, []authorizationCall{{
		workspaceID: "wsp_1", agentID: "agt_1", installationID: "ins_github",
		requiredScopes: []string{app.ServiceEventSubscriptionScopeWrite},
	}}, store.authorizationCalls)
	require.Equal(t, []targetCall{{
		agent: agent.AuthenticatedAgent{
			WorkspaceID: "wsp_1", AgentID: "agt_1", MemberID: "mem_agent",
		},
		target: "#agentteam-in-agentteam:31e0897c",
	}}, store.targetCalls)
	require.Len(t, store.createCalls, 1)
	create := store.createCalls[0]
	require.Equal(t, "github", create.Provider)
	require.Equal(t, "ins_github", create.InstallationID)
	require.Equal(t, expectedActor(), create.Actor)
	require.Equal(t, store.resolvedTarget.TargetKind, create.Target.TargetKind)
	require.Equal(t, store.resolvedTarget.TargetRef, create.Target.TargetRef)
	require.Equal(t, "#agentteam-in-agentteam:31e0897c", create.Target.ScopeSnapshot["target_display"])
	require.Equal(t, "exact", create.Target.ScopeSnapshot["target_confidence"])
	require.Equal(t, "https://github.com/PingCAP/AgentTeam/pull/42", create.ResourceRef)
	require.Equal(t, "github:pingcap/agentteam#42", create.ResourceKey)
	require.Equal(t, map[string]any{"events": []string{"comments", "reviews"}}, create.EventFilters)
	require.True(t, create.ValidateResource)
	require.NotNil(t, create.NotifyPrincipal)
	require.False(t, *create.NotifyPrincipal)
	require.Equal(t, createdViaAppCommand, create.CreatedVia)
}

func TestInvokeWatchPullRequestValidatesExplicitEvents(t *testing.T) {
	tests := []struct {
		name       string
		eventsJSON string
		wantEvents []string
		wantErr    bool
	}{
		{name: "omitted events use store defaults"},
		{
			name:       "supported aliases become canonical",
			eventsJSON: `,"events":[" COMMENT ","sync","base-branch-push"]`,
			wantEvents: []string{"base_branch_push", "comments", "synchronize"},
		},
		{name: "empty explicit list", eventsJSON: `,"events":[]`, wantErr: true},
		{
			name:       "blank explicit event",
			eventsJSON: `,"events":[" "]`,
			wantErr:    true,
		},
		{
			name:       "unknown event",
			eventsJSON: `,"events":["bogus_event"]`,
			wantErr:    true,
		},
	}
	for _, test := range tests {
		t.Run(test.name, func(t *testing.T) {
			store := newAuthorizedFakeStore("gitea")
			store.resolvedTarget = app.ServiceEventTarget{
				TargetKind: "thread", TargetRef: "scp_thread",
			}
			store.createResult = app.ServiceEventSubscriptionResult{
				Subscription: app.ServiceEventSubscriptionRecord{
					ID: "pwt_events", Provider: "gitea",
					AppInstallationID: "ins_gitea", Status: "active",
				},
			}

			_, err := New(store).Invoke(context.Background(), Invocation{
				Provider: "gitea", WorkspaceID: "wsp_1", AgentID: "agt_1",
				AgentMemberID: "mem_agent", ServiceInstallationID: "ins_gitea",
				Action: ActionWatchPullRequest,
				Input: json.RawMessage(`{
					"target":"#agentteam-in-agentteam:31e0897c",
					"pullRequest":"pingcap/agentteam#42"` + test.eventsJSON + `
				}`),
			})

			if test.wantErr {
				require.ErrorIs(t, err, storeerr.ErrBadRequest)
				require.Empty(t, store.createCalls)
				return
			}
			require.NoError(t, err)
			require.Len(t, store.createCalls, 1)
			if len(test.wantEvents) == 0 {
				require.Empty(t, store.createCalls[0].EventFilters)
				return
			}
			require.Equal(
				t,
				map[string]any{"events": test.wantEvents},
				store.createCalls[0].EventFilters,
			)
		})
	}
}

func TestClassifyInvocationErrorCoversProviderFailuresAndConflict(t *testing.T) {
	tests := []struct {
		name string
		err  error
		code extensionflow.InvocationErrorCode
	}{
		{
			name: "provider auth",
			err:  app.ErrPullRequestProviderAuthUnavailable,
			code: extensionflow.InvocationProviderAuthUnavailable,
		},
		{
			name: "provider rate limit",
			err:  app.ErrPullRequestProviderRateLimited,
			code: extensionflow.InvocationRateLimited,
		},
		{
			name: "provider upstream",
			err:  app.ErrPullRequestProviderUpstreamFailure,
			code: extensionflow.InvocationUpstreamFailure,
		},
		{
			name: "ambiguous selector",
			err:  storeerr.Conflict("secret ambiguity"),
			code: extensionflow.InvocationConflict,
		},
	}
	for _, test := range tests {
		t.Run(test.name, func(t *testing.T) {
			err := ClassifyInvocationError(test.err)
			code, ok := extensionflow.InvocationErrorCodeOf(err)
			require.True(t, ok)
			require.Equal(t, test.code, code)
			require.NotContains(t, err.Error(), "secret")
		})
	}
}

func TestInvokeListWatchesBindsExactInstallationTargetAndResource(t *testing.T) {
	store := newAuthorizedFakeStore("gitea")
	store.resolvedTarget = app.ServiceEventTarget{
		TargetKind: "task", TargetRef: "tsk_scope",
	}
	store.listPage = app.ServiceEventSubscriptionPage{
		Subscriptions: []app.ServiceEventSubscriptionRecord{{
			ID: "pwt_2", Provider: "gitea", AppInstallationID: "ins_gitea",
			TargetKind: "task", TargetRef: "tsk_scope",
			ResourceRef: "pingcap/agentteam#7", ResourceKey: "gitea:pingcap/agentteam#7",
			Status: "paused",
			ScopeSnapshot: []byte(`{
				"target_display":"#agentteam-in-agentteam/task/2470",
				"target_confidence":"exact"
			}`),
		}},
		Limit: 25, Cursor: "10", HasMore: true, NextCursor: "35",
	}
	service := New(store)

	data, err := service.Invoke(context.Background(), Invocation{
		Provider: "gitea", WorkspaceID: "wsp_1", AgentID: "agt_1",
		AgentMemberID: "mem_agent", ServiceInstallationID: "ins_gitea",
		Action: "list-watches",
		Input: json.RawMessage(`{
			"target":"#agentteam-in-agentteam/task/2470",
			"resource":"https://git.example.test/PingCAP/AgentTeam/pulls/7",
			"status":"paused",
			"limit":25,
			"cursor":"10"
		}`),
	})
	require.NoError(t, err)
	require.JSONEq(t, `{
		"watches":[{
			"watchId":"pwt_2",
			"provider":"gitea",
			"target":"#agentteam-in-agentteam/task/2470",
			"pullRequest":"pingcap/agentteam#7",
			"resource":"gitea:pingcap/agentteam#7",
			"status":"paused"
		}],
		"limit":25,
		"cursor":"10",
		"hasMore":true,
		"nextCursor":"35"
	}`, string(data))

	require.Equal(t, []authorizationCall{{
		workspaceID: "wsp_1", agentID: "agt_1", installationID: "ins_gitea",
		requiredScopes: []string{app.ServiceEventSubscriptionScopeRead},
	}}, store.authorizationCalls)
	require.Equal(t, []listCall{{
		workspaceID: "wsp_1", actor: expectedActor(), installationID: "ins_gitea",
		targetKind: "task", targetRef: "tsk_scope", status: "paused",
		options: app.ListServiceEventSubscriptionsOptions{
			ResourceKey: "gitea:pingcap/agentteam#7", Limit: 25, Cursor: "10", Page: true,
		},
	}}, store.listCalls)
}

func TestResolveMutationSubscriptionRequiresExactTargetAndResourceMatch(t *testing.T) {
	matching := app.ServiceEventSubscriptionRecord{
		ID: "pwt_match", Provider: "gitea", AppInstallationID: "ins_gitea",
		TargetKind: "thread", TargetRef: "scp_thread",
		ResourceKey: "gitea:pingcap/agentteam#8",
	}
	tests := []struct {
		name          string
		subscriptions []app.ServiceEventSubscriptionRecord
		wantID        string
		wantErr       error
	}{
		{name: "no match", wantErr: storeerr.ErrNotFound},
		{
			name:          "one match",
			subscriptions: []app.ServiceEventSubscriptionRecord{matching},
			wantID:        "pwt_match",
		},
		{
			name: "multiple matches",
			subscriptions: []app.ServiceEventSubscriptionRecord{
				matching,
				{
					ID: "pwt_second", Provider: "gitea",
					AppInstallationID: "ins_gitea",
					TargetKind:        "thread", TargetRef: "scp_thread",
					ResourceKey: "gitea:pingcap/agentteam#8",
				},
			},
			wantErr: storeerr.ErrConflict,
		},
	}
	for _, test := range tests {
		t.Run(test.name, func(t *testing.T) {
			store := newAuthorizedFakeStore("gitea")
			store.resolvedTarget = app.ServiceEventTarget{
				TargetKind: "thread", TargetRef: "scp_thread",
			}
			store.listPage = app.ServiceEventSubscriptionPage{
				Subscriptions: test.subscriptions,
			}
			invocation, _, err := normalizeInvocation(Invocation{
				Provider: "gitea", WorkspaceID: "wsp_1", AgentID: "agt_1",
				AgentMemberID: "mem_agent", ServiceInstallationID: "ins_gitea",
				Action: ActionPauseWatch,
			})
			require.NoError(t, err)

			subscription, err := New(store).resolveMutationSubscription(
				context.Background(),
				invocation,
				expectedActor(),
				mutationInput{
					Target: "#agentteam-in-agentteam:31e0897c", PullRequest: "pingcap/agentteam#8",
				},
			)

			if test.wantErr != nil {
				require.True(t, errors.Is(err, test.wantErr), err)
			} else {
				require.NoError(t, err)
				require.Equal(t, test.wantID, subscription.ID)
			}
			require.Equal(t, 2, store.listCalls[0].options.Limit)
		})
	}
}

func TestInvokeLifecycleActionsPreserveProviderInstallationAndActorBinding(t *testing.T) {
	t.Run("pause by watch id", func(t *testing.T) {
		store := newAuthorizedFakeStore("github")
		store.getResult = app.ServiceEventSubscriptionRecord{
			ID: "pwt_pause", Provider: "github", AppInstallationID: "ins_github",
		}

		data, err := New(store).Invoke(context.Background(), Invocation{
			Provider: "github", WorkspaceID: "wsp_1", AgentID: "agt_1",
			AgentMemberID: "mem_agent", ServiceInstallationID: "ins_github",
			Action: "pause-watch", Input: json.RawMessage(`{"watchId":"pwt_pause"}`),
		})
		require.NoError(t, err)
		require.JSONEq(t, `{"watchId":"pwt_pause","status":"paused"}`, string(data))
		require.Equal(t, []getCall{{
			workspaceID: "wsp_1", actor: expectedActor(), provider: "github",
			installationID: "ins_github", watchID: "pwt_pause",
			requiredScopes: []string{app.ServiceEventSubscriptionScopeWrite},
		}}, store.getCalls)
		require.Equal(t, []lifecycleCall{{
			workspaceID: "wsp_1", actor: expectedActor(), watchID: "pwt_pause",
			reason: createdViaAppCommand,
		}}, store.pauseCalls)
	})

	t.Run("resume by exact target and resource", func(t *testing.T) {
		store := newAuthorizedFakeStore("gitea")
		store.resolvedTarget = app.ServiceEventTarget{
			TargetKind: "thread", TargetRef: "scp_thread",
		}
		store.listPage = app.ServiceEventSubscriptionPage{
			Subscriptions: []app.ServiceEventSubscriptionRecord{{
				ID: "pwt_resume", Provider: "gitea", AppInstallationID: "ins_gitea",
				TargetKind: "thread", TargetRef: "scp_thread",
				ResourceKey: "gitea:pingcap/agentteam#8",
			}},
		}
		store.getResult = store.listPage.Subscriptions[0]

		data, err := New(store).Invoke(context.Background(), Invocation{
			Provider: "gitea", WorkspaceID: "wsp_1", AgentID: "agt_1",
			AgentMemberID: "mem_agent", ServiceInstallationID: "ins_gitea",
			Action: "resume-watch",
			Input: json.RawMessage(`{
				"target":"#agentteam-in-agentteam:31e0897c",
				"pullRequest":"PingCAP/AgentTeam#8"
			}`),
		})
		require.NoError(t, err)
		require.JSONEq(t, `{"watchId":"pwt_resume","status":"resumed"}`, string(data))
		require.Equal(t, []listCall{{
			workspaceID: "wsp_1", actor: expectedActor(), installationID: "ins_gitea",
			targetKind: "thread", targetRef: "scp_thread",
			options: app.ListServiceEventSubscriptionsOptions{
				ResourceKey: "gitea:pingcap/agentteam#8", Limit: 2,
			},
		}}, store.listCalls)
		require.Equal(t, []lifecycleCall{{
			workspaceID: "wsp_1", actor: expectedActor(), watchID: "pwt_resume",
		}}, store.resumeCalls)
	})

	t.Run("unwatch by watch id", func(t *testing.T) {
		store := newAuthorizedFakeStore("github")
		store.getResult = app.ServiceEventSubscriptionRecord{
			ID: "pwt_remove", Provider: "github", AppInstallationID: "ins_github",
		}

		data, err := New(store).Invoke(context.Background(), Invocation{
			Provider: "github", WorkspaceID: "wsp_1", AgentID: "agt_1",
			AgentMemberID: "mem_agent", ServiceInstallationID: "ins_github",
			Action: "unwatch", Input: json.RawMessage(`{"watchId":"pwt_remove"}`),
		})
		require.NoError(t, err)
		require.JSONEq(t, `{"watchId":"pwt_remove","status":"deleted"}`, string(data))
		require.Equal(t, []lifecycleCall{{
			workspaceID: "wsp_1", actor: expectedActor(), watchID: "pwt_remove",
		}}, store.removeCalls)
	})
}

func TestInvokeRejectsAuthorizationIdentityMismatchBeforeWatchOperations(t *testing.T) {
	tests := []struct {
		name   string
		mutate func(*app.AgentAppAuthorizationRecord)
	}{
		{name: "workspace", mutate: func(auth *app.AgentAppAuthorizationRecord) {
			auth.WorkspaceID = "wsp_other"
		}},
		{name: "agent", mutate: func(auth *app.AgentAppAuthorizationRecord) {
			auth.AgentID = "agt_other"
		}},
		{name: "agent member", mutate: func(auth *app.AgentAppAuthorizationRecord) {
			auth.AgentMemberID = "mem_other"
		}},
		{name: "provider slug", mutate: func(auth *app.AgentAppAuthorizationRecord) {
			auth.AppSlug = "gitea"
		}},
		{name: "provider definition", mutate: func(auth *app.AgentAppAuthorizationRecord) {
			auth.AppID = "app_gitea"
		}},
		{name: "installation", mutate: func(auth *app.AgentAppAuthorizationRecord) {
			auth.AppInstallationID = "ins_other"
		}},
	}
	for _, test := range tests {
		t.Run(test.name, func(t *testing.T) {
			store := newAuthorizedFakeStore("github")
			test.mutate(&store.authorization)

			_, err := New(store).Invoke(context.Background(), Invocation{
				Provider: "github", WorkspaceID: "wsp_1", AgentID: "agt_1",
				AgentMemberID: "mem_agent", ServiceInstallationID: "ins_github",
				Action: "watch-pull-request",
				Input: json.RawMessage(`{
					"target":"#agentteam-in-agentteam:31e0897c",
					"pullRequest":"pingcap/agentteam#1"
				}`),
			})
			require.ErrorIs(t, err, appAuthorizationIdentityMismatch)
			require.Empty(t, store.targetCalls)
			require.Empty(t, store.createCalls)
		})
	}
}

func TestInvokeDoesNotUseLegacyMCPToolsAsExtensionActionGate(t *testing.T) {
	store := newAuthorizedFakeStore("github")
	store.authorization.MCPTools = nil
	store.listPage = app.ServiceEventSubscriptionPage{
		Subscriptions: []app.ServiceEventSubscriptionRecord{},
		Limit:         20,
	}

	data, err := New(store).Invoke(context.Background(), Invocation{
		Provider: "github", WorkspaceID: "wsp_1", AgentID: "agt_1",
		AgentMemberID: "mem_agent", ServiceInstallationID: "ins_github",
		Action: ActionListWatches, Input: json.RawMessage(`{}`),
	})

	require.NoError(t, err)
	require.JSONEq(t, `{"watches":[],"limit":20}`, string(data))
	require.Equal(t, []authorizationCall{{
		workspaceID: "wsp_1", agentID: "agt_1", installationID: "ins_github",
		requiredScopes: []string{app.ServiceEventSubscriptionScopeRead},
	}}, store.authorizationCalls)
}

func TestRequiredTargetRequiresExplicitValue(t *testing.T) {
	_, err := requiredTarget("")
	require.Error(t, err)

	target, err := requiredTarget(" #agentteam-in-agentteam:31e0897c ")
	require.NoError(t, err)
	require.Equal(t, "#agentteam-in-agentteam:31e0897c", target)
}

func newAuthorizedFakeStore(provider string) *fakeStore {
	installationID := "ins_" + provider
	return &fakeStore{authorization: app.AgentAppAuthorizationRecord{
		WorkspaceID: "wsp_1", AgentID: "agt_1", AgentMemberID: "mem_agent",
		AppID: "app_" + provider, AppSlug: provider,
		AppInstallationID: installationID, AuthorizedByMemberID: "mem_authorizer",
		MCPTools: []string{
			"watch_pull_request", "list_watches", "pause_watch", "resume_watch", "unwatch",
		},
	}}
}

func expectedActor() app.ServiceEventActor {
	return app.ServiceEventActor{
		ActorType: "agent", ActorID: "agt_1", MemberID: "mem_agent",
		AuthorizerUserID: "mem_authorizer",
	}
}
