package extensione2e

import (
	"context"
	"encoding/json"
	"io"
	"net"
	"net/http"
	"net/http/httptest"
	"net/url"
	"os"
	"strings"
	"sync"
	"testing"
	"time"

	"github.com/gin-gonic/gin"
	"github.com/stretchr/testify/require"

	"github.com/tidbcloud/AgentTeam/desktop/internal/api/exec"
	"github.com/tidbcloud/AgentTeam/desktop/internal/types"
	"github.com/tidbcloud/AgentTeam/desktop/internal/wire/apiclient"
	"github.com/tidbcloud/AgentTeam/internal/api/extension"
	"github.com/tidbcloud/AgentTeam/internal/coredb"
	"github.com/tidbcloud/AgentTeam/internal/coredb/testkit"
	"github.com/tidbcloud/AgentTeam/internal/extension/egress"
	"github.com/tidbcloud/AgentTeam/internal/extension/registry"
	"github.com/tidbcloud/AgentTeam/internal/extension/spec"
	"github.com/tidbcloud/AgentTeam/internal/service/extensionflow"
	"github.com/tidbcloud/AgentTeam/internal/store/agent"
	"github.com/tidbcloud/AgentTeam/internal/store/serviceactor"
)

const (
	e2eWorkspace = "ws_extension_full_e2e"
	e2eAgent     = "agt_extension_full_e2e"
	e2eMember    = "mem_extension_full_e2e"
	e2eMachine   = "mach_extension_full_e2e"
	e2eToken     = "agent-runtime-token"

	e2eHostDefinitionID       = "app_extension_full_e2e"
	e2eHostInstallationID     = "inst_extension_full_e2e"
	e2eHostConnectionID       = "conn_extension_full_e2e"
	e2eHostSecondConnectionID = "conn_extension_full_e2e_second"
	e2eHostSelectionID        = "selection_extension_full_e2e"
	e2eHostAuthorization      = "authz_extension_full_e2e"
	e2eHostSubject            = "provider-user-extension-full-e2e"
	e2eHostSecondSubject      = "provider-user-extension-full-e2e-second"
)

var extensionE2ETiDB testkit.TiDBCoreDBFixture

func TestMain(main *testing.M) {
	os.Exit(testkit.RunTestsWithTiDBCoreDBFixture(
		main,
		"desktop_extension_full_e2e",
		&extensionE2ETiDB,
	))
}

func TestExtensionCatalogMachineHintWireCarriesOnlyVersion(t *testing.T) {
	encoded, err := json.Marshal(types.ExtensionCatalogHint{Version: 17})
	require.NoError(t, err)
	require.JSONEq(t, `{"version":17}`, string(encoded))

	var fields map[string]json.RawMessage
	require.NoError(t, json.Unmarshal(encoded, &fields))
	require.Len(t, fields, 1)
	require.Contains(t, fields, "version")
}

// TestAppHostedDesktopInvokeAuditBudgetAndOfflineRevoke is the App-hosted
// production-chain acceptance test:
//
// App Definition/Installation/Connection/authorization + selected Extension ->
// authoritative flattened Catalog -> Desktop verified snapshot -> process
// Exec authorization -> deterministic server bind -> guarded TLS provider ->
// bounded response + durable secret-free audit.
//
// It then disables the host App authorization while the Desktop intentionally
// retains its old process snapshot (notification/offline delay), proving the
// next call is blocked server-side before provider/audit. Finally the reconnect
// hint is fetched and the old local Exec authorization is revoked.
func TestAppHostedDesktopInvokeAuditBudgetAndOfflineRevoke(t *testing.T) {
	ctx := context.Background()
	db := extensionE2ETiDB.RollbackDB(t, ctx)
	now := time.Date(2026, 8, 11, 18, 0, 0, 0, time.UTC)
	seedExtensionE2E(t, db, now)

	providerRecorder := &providerRecorder{}
	provider := httptest.NewTLSServer(providerRecorder)
	defer provider.Close()
	providerOrigin := publicProviderOrigin(t, provider)

	extensionRegistry := registry.New()
	pkg := extensionE2EPackage(t, providerOrigin)
	registered, err := extensionRegistry.RegisterExternal(pkg)
	require.NoError(t, err)
	credentialKey := []byte("0123456789abcdef0123456789abcdef")
	service := extensionflow.New(db, extensionRegistry, credentialKey, extensionflow.Options{
		Now: func() time.Time { return now },
		EgressFactory: func(plan registry.ActionPlan) (extensionflow.InvocationEgress, error) {
			origin, parseErr := url.Parse(plan.Endpoint.BaseURL)
			if parseErr != nil {
				return nil, parseErr
			}
			tlsConfig := provider.Client().Transport.(*http.Transport).
				TLSClientConfig.Clone()
			tlsConfig.ServerName = "example.com"
			return egress.NewClient(egress.Config{
				Origin:          origin,
				Resolver:        publicTestResolver{},
				DialContext:     localServerDialer(provider),
				TLSClientConfig: tlsConfig,
			})
		},
	})
	seedAppHostedExtension(
		t,
		ctx,
		db,
		service,
		registered,
		credentialKey,
		now,
	)
	requireAppHostedIdentityOnly(t, ctx, db)

	access := &catalogAdapter{service: service}
	gin.SetMode(gin.ReleaseMode)
	serverRouter := gin.New()
	extension.RegisterRoutes(serverRouter, extension.Dependencies{
		Auth:     e2eAuthenticator{},
		Catalog:  extensionRegistry,
		Access:   access,
		Snapshot: access,
		Invoker:  service,
	})
	invokeRequests := &serverInvokeRecorder{next: serverRouter}
	agentteamServer := httptest.NewServer(invokeRequests)
	defer agentteamServer.Close()
	serverClient := &apiclient.Client{
		ServerURL: agentteamServer.URL, AgentID: e2eAgent, AuthToken: e2eToken,
		RouteWorkspaceID: e2eWorkspace, HTTPClient: agentteamServer.Client(),
	}

	granted := requireCatalogSnapshot(t, ctx, service)
	require.True(t, granted.Enabled)
	desktopSnapshot, diagnostics, modified, err :=
		exec.FetchExtensionSnapshot(
			ctx,
			serverClient,
			exec.ExtensionSnapshot{},
		)
	require.NoError(t, err)
	require.Empty(t, diagnostics, "granted Catalog must fetch without skipped units")
	require.True(t, modified)
	require.NotEmpty(t, desktopSnapshot.ETag())
	require.Equal(t, []string{"e2e-provider"}, desktopSnapshot.ExtensionNames())
	require.Equal(t, []string{"create-item"}, desktopSnapshot.ActionNames("e2e-provider"))
	requireFlattenedDesktopProjection(t, desktopSnapshot)
	catalogExchanges := invokeRequests.catalogSnapshot()
	require.Len(t, catalogExchanges, 1)
	requireCatalogResponse(
		t,
		catalogExchanges[0],
		"",
		http.StatusOK,
		desktopSnapshot.ETag(),
		[]string{"e2e-provider"},
	)

	runtime := newDesktopExecRuntime(
		t,
		serverClient,
		desktopSnapshot,
		"process-1",
	)
	defer runtime.close()
	discovered, err := runtime.client.FetchRuntimeCatalog(
		ctx,
		runtime.baseURL,
		runtime.credential.Token,
	)
	require.NoError(t, err)
	extensionAction, found := discovered.Lookup("e2e-provider", "create-item")
	require.True(t, found)
	require.Equal(t, exec.RuntimeActionExtension, extensionAction.Source)
	require.NotContains(t, discovered.RenderRootHelp(), provider.URL)
	requireFlattenedDesktopProjection(t, desktopSnapshot)

	// A second active connection does not alter the flattened Catalog, so an
	// already-running Desktop still holds a current snapshot. The Server must
	// nevertheless resolve provider credentials authoritatively on every call
	// and reject ambiguity before egress or audit.
	secondConnectionID := seedSecondActiveAppConnection(
		t,
		ctx,
		db,
		credentialKey,
		now.Add(time.Second),
	)
	ambiguous := requireCatalogSnapshot(t, ctx, service)
	require.Equal(t, granted.Version, ambiguous.Version)
	require.Equal(t, granted.State, ambiguous.State)
	require.True(t, ambiguous.Enabled)
	ambiguousDesktop, diagnostics, modified, err := exec.FetchExtensionSnapshot(
		ctx,
		serverClient,
		desktopSnapshot,
	)
	require.NoError(t, err)
	require.Empty(t, diagnostics, "resolved Catalog must fetch without skipped units")
	require.False(t, modified, "unchanged public Catalog must reuse its ETag cache")
	require.Equal(t, desktopSnapshot.ETag(), ambiguousDesktop.ETag())
	requireFlattenedDesktopProjection(t, ambiguousDesktop)
	catalogExchanges = invokeRequests.catalogSnapshot()
	require.Len(t, catalogExchanges, 2)
	requireCatalogResponse(
		t,
		catalogExchanges[1],
		desktopSnapshot.ETag(),
		http.StatusNotModified,
		desktopSnapshot.ETag(),
		nil,
	)
	ambiguousInput := json.RawMessage(
		`{"owner":"pingcap","title":"ambiguous","metadata":{}}`,
	)
	_, err = runtime.client.Call(
		ctx,
		runtime.baseURL,
		runtime.credential.Token,
		"e2e-provider",
		"create-item",
		ambiguousInput,
	)
	var execErr *exec.Error
	require.ErrorAs(t, err, &execErr)
	require.Equal(t, exec.CodeProviderAuthUnavailable, execErr.Code)
	for _, secret := range appHostSecrets() {
		require.NotContains(t, err.Error(), secret)
	}
	require.Zero(t, providerRecorder.count())
	var auditsDuringAmbiguity int
	require.NoError(t, db.QueryRow(ctx, `
SELECT COUNT(*)
FROM extension_invocation_audits
WHERE workspace_id=$1 AND agent_id=$2`,
		e2eWorkspace,
		e2eAgent,
	).Scan(&auditsDuringAmbiguity))
	require.Zero(t, auditsDuringAmbiguity)
	require.NoError(
		t,
		serviceactor.New(db).DisableConnection(ctx, secondConnectionID),
	)
	recovered := requireCatalogSnapshot(t, ctx, service)
	require.Equal(t, granted.Version, recovered.Version)
	require.Equal(t, granted.State, recovered.State)

	callerControlledTransport := json.RawMessage(
		`{"owner":"pingcap","title":"hello","metadata":{},` +
			`"url":"https://attacker.invalid","headers":{"Authorization":"steal"}}`,
	)
	require.Error(
		t,
		discovered.ValidateInput("e2e-provider", "create-item", callerControlledTransport),
	)
	_, err = runtime.client.Call(
		ctx,
		runtime.baseURL,
		runtime.credential.Token,
		"e2e-provider",
		"create-item",
		callerControlledTransport,
	)
	require.Error(t, err)
	require.Zero(t, providerRecorder.count(), "caller-controlled URL/header input must fail locally")

	firstInput := json.RawMessage(
		`{"owner":"pingcap","labels":["bug","p0"],"title":"hello","metadata":{"z":2,"a":1}}`,
	)
	secondInput := json.RawMessage(
		`{"metadata":{"a":1,"z":2},"title":"hello","labels":["bug","p0"],"owner":"pingcap"}`,
	)
	for _, input := range []json.RawMessage{firstInput, secondInput} {
		require.NoError(t, discovered.ValidateInput("e2e-provider", "create-item", input))
		result, callErr := runtime.client.Call(
			ctx,
			runtime.baseURL,
			runtime.credential.Token,
			"e2e-provider",
			"create-item",
			input,
		)
		require.NoError(t, callErr)
		require.Contains(t, string(result), `"ok":true`)
		require.NotContains(t, string(result), "provider-secret")
		require.NotContains(t, string(result), provider.URL)
		for _, hostIdentity := range appHostSecrets() {
			require.NotContains(t, string(result), hostIdentity)
		}
	}
	requests := providerRecorder.snapshot()
	require.Len(t, requests, 2)
	require.Equal(t, http.MethodPost, requests[0].Method)
	require.Equal(t, "/v1/repos/pingcap/items", requests[0].Path)
	require.Equal(t, "labels=bug&labels=p0", requests[0].RawQuery)
	require.Equal(t, `{"metadata":{"a":1,"z":2},"title":"hello"}`, requests[0].Body)
	require.Equal(t, requests[0].Method, requests[1].Method)
	require.Equal(t, requests[0].Path, requests[1].Path)
	require.Equal(t, requests[0].RawQuery, requests[1].RawQuery)
	require.Equal(t, requests[0].Body, requests[1].Body)
	require.Equal(t, "Bearer provider-secret", requests[0].Authorization)
	requireInvocationPayloadsHideAppHost(t, invokeRequests.snapshot())

	var (
		successAudits, distinctHashes int
		distinctDefinitionVersions    int
		auditedDefinitionVersion      int
		distinctDefinitionDigests     int
		auditedDefinitionDigest       string
		distinctCatalogVersions       int
		auditedCatalogVersion         int64
	)
	require.NoError(t, db.QueryRow(ctx, `
SELECT
  SUM(CASE WHEN result_classification='success' THEN 1 ELSE 0 END),
  COUNT(DISTINCT request_hash),
  COUNT(DISTINCT definition_version),
  MAX(definition_version),
  COUNT(DISTINCT definition_digest),
  MAX(definition_digest),
  COUNT(DISTINCT catalog_version),
  MAX(catalog_version)
FROM extension_invocation_audits
WHERE workspace_id=$1 AND agent_id=$2`,
		e2eWorkspace,
		e2eAgent,
	).Scan(
		&successAudits,
		&distinctHashes,
		&distinctDefinitionVersions,
		&auditedDefinitionVersion,
		&distinctDefinitionDigests,
		&auditedDefinitionDigest,
		&distinctCatalogVersions,
		&auditedCatalogVersion,
	))
	require.Equal(t, 2, successAudits)
	require.Equal(t, 1, distinctHashes, "semantic-equivalent input must audit one deterministic request hash")
	require.Equal(t, 1, distinctDefinitionVersions)
	require.Equal(t, registered.Definition.Version, auditedDefinitionVersion)
	require.Equal(t, 1, distinctDefinitionDigests)
	require.Equal(t, registered.Definition.Digest, auditedDefinitionDigest)
	require.Equal(t, 1, distinctCatalogVersions)
	require.Equal(t, granted.Version, auditedCatalogVersion)

	providerRecorder.setOversize(true)
	_, err = runtime.client.Call(
		ctx,
		runtime.baseURL,
		runtime.credential.Token,
		"e2e-provider",
		"create-item",
		firstInput,
	)
	require.Error(t, err)
	require.NotContains(t, err.Error(), "provider-secret")
	require.NotContains(t, err.Error(), provider.URL)
	require.Equal(t, 3, providerRecorder.count())
	var budgetAudits int
	require.NoError(t, db.QueryRow(ctx, `
SELECT COUNT(*)
FROM extension_invocation_audits
WHERE workspace_id=$1 AND agent_id=$2
  AND result_classification='upstream_failure'`,
		e2eWorkspace,
		e2eAgent,
	).Scan(&budgetAudits))
	require.Equal(t, 1, budgetAudits)

	// Keep the old Desktop token/snapshot alive to model delayed notification
	// or an offline machine. The committed App authorization revoke must still
	// block it.
	disableAppAuthorization(t, ctx, db, service, now.Add(time.Minute))
	callsBeforeRevokedInvoke := providerRecorder.count()
	_, err = runtime.client.Call(
		ctx,
		runtime.baseURL,
		runtime.credential.Token,
		"e2e-provider",
		"create-item",
		firstInput,
	)
	require.Error(t, err)
	require.Equal(t, callsBeforeRevokedInvoke, providerRecorder.count())
	var auditsAfterRevoke int
	require.NoError(t, db.QueryRow(ctx, `
SELECT COUNT(*)
FROM extension_invocation_audits
WHERE workspace_id=$1 AND agent_id=$2`,
		e2eWorkspace,
		e2eAgent,
	).Scan(&auditsAfterRevoke))
	require.Equal(t, 3, auditsAfterRevoke, "pre-egress revoke rejection must not create a provider-attempt audit")

	revoked := requireCatalogSnapshot(t, ctx, service)
	require.False(t, revoked.Enabled)
	require.Greater(t, revoked.Version, granted.Version)
	revokedSnapshot, diagnostics, modified, err :=
		exec.FetchExtensionSnapshot(ctx, serverClient, desktopSnapshot)
	require.NoError(t, err)
	require.Empty(t, diagnostics, "disabled Catalog must fetch without skipped units")
	require.True(t, modified)
	require.True(t, revokedSnapshot.Empty())
	require.NotEqual(t, desktopSnapshot.ETag(), revokedSnapshot.ETag())
	catalogExchanges = invokeRequests.catalogSnapshot()
	require.Len(t, catalogExchanges, 3)
	requireCatalogResponse(
		t,
		catalogExchanges[2],
		desktopSnapshot.ETag(),
		http.StatusOK,
		revokedSnapshot.ETag(),
		nil,
	)
	runtime.revoke(t)
	_, err = runtime.client.FetchRuntimeCatalog(
		ctx,
		runtime.baseURL,
		runtime.credential.Token,
	)
	require.Error(t, err, "reconnect convergence must retire the old process capability")
}

type e2eAuthenticator struct{}

func (e2eAuthenticator) AuthenticateAgent(
	_ context.Context,
	agentID string,
	bearerToken string,
) (agent.AuthenticatedAgent, error) {
	if agentID != e2eAgent || bearerToken != e2eToken {
		return agent.AuthenticatedAgent{}, agent.ErrUnauthorized
	}
	return agent.AuthenticatedAgent{
		AgentID: e2eAgent, WorkspaceID: e2eWorkspace,
		MemberID: e2eMember, MachineID: e2eMachine,
	}, nil
}

type catalogAdapter struct {
	service *extensionflow.Service
}

func (adapter *catalogAdapter) ActiveAgentDomains(
	ctx context.Context,
	workspaceID string,
	agentID string,
) ([]extension.AgentDomainAccess, error) {
	snapshot, err := adapter.AgentCatalogSnapshot(ctx, workspaceID, agentID)
	return snapshot.Domains, err
}

func (adapter *catalogAdapter) AgentCatalogSnapshot(
	ctx context.Context,
	workspaceID string,
	agentID string,
) (extension.CatalogSnapshot, error) {
	snapshot, err := adapter.service.AgentCatalogSnapshot(ctx, workspaceID, agentID)
	if err != nil {
		return extension.CatalogSnapshot{}, err
	}
	domains := make([]extension.AgentDomainAccess, 0, len(snapshot.Entries))
	for _, entry := range snapshot.Entries {
		domains = append(domains, extension.AgentDomainAccess{
			Domain:            entry.Domain,
			DefinitionVersion: entry.DefinitionVersion,
			DefinitionDigest:  entry.DefinitionDigest,
			ExtensionName:     entry.ExtensionName,
			Actions:           append([]string(nil), entry.Actions...),
		})
	}
	return extension.CatalogSnapshot{Domains: domains}, nil
}

type providerRequest struct {
	Method        string
	Path          string
	RawQuery      string
	Body          string
	Authorization string
}

type publicTestResolver struct{}

func (publicTestResolver) LookupIP(
	context.Context,
	string,
	string,
) ([]net.IP, error) {
	return []net.IP{net.ParseIP("8.8.8.8")}, nil
}

func publicProviderOrigin(t *testing.T, server *httptest.Server) string {
	t.Helper()
	_, port, err := net.SplitHostPort(server.Listener.Addr().String())
	require.NoError(t, err)
	return (&url.URL{
		Scheme: "https",
		Host:   net.JoinHostPort("desktop-provider.example", port),
	}).String()
}

func localServerDialer(
	server *httptest.Server,
) func(context.Context, string, string) (net.Conn, error) {
	address := server.Listener.Addr().String()
	return func(ctx context.Context, network, _ string) (net.Conn, error) {
		return (&net.Dialer{}).DialContext(ctx, network, address)
	}
}

type providerRecorder struct {
	mu       sync.Mutex
	requests []providerRequest
	oversize bool
}

func (recorder *providerRecorder) ServeHTTP(
	writer http.ResponseWriter,
	request *http.Request,
) {
	body, _ := io.ReadAll(request.Body)
	recorder.mu.Lock()
	recorder.requests = append(recorder.requests, providerRequest{
		Method: request.Method, Path: request.URL.EscapedPath(),
		RawQuery: request.URL.RawQuery, Body: string(body),
		Authorization: request.Header.Get("Authorization"),
	})
	oversize := recorder.oversize
	recorder.mu.Unlock()
	writer.Header().Set("Content-Type", "application/json")
	if oversize {
		_, _ = io.WriteString(writer, `"`+strings.Repeat("x", egress.MaxFinalResponseBytes)+`"`)
		return
	}
	_, _ = io.WriteString(writer, `{"ok":true}`)
}

func (recorder *providerRecorder) setOversize(value bool) {
	recorder.mu.Lock()
	recorder.oversize = value
	recorder.mu.Unlock()
}

func (recorder *providerRecorder) snapshot() []providerRequest {
	recorder.mu.Lock()
	defer recorder.mu.Unlock()
	return append([]providerRequest(nil), recorder.requests...)
}

func (recorder *providerRecorder) count() int {
	recorder.mu.Lock()
	defer recorder.mu.Unlock()
	return len(recorder.requests)
}

func requireCatalogSnapshot(
	t *testing.T,
	ctx context.Context,
	service *extensionflow.Service,
) extensionflow.AgentCatalogSnapshot {
	t.Helper()
	snapshot, err := service.AgentCatalogSnapshot(ctx, e2eWorkspace, e2eAgent)
	require.NoError(t, err)
	return snapshot
}

func extensionE2EPackage(t *testing.T, endpoint string) spec.Package {
	return extensionE2EPackageVersion(t, endpoint, 1)
}

func extensionE2EPackageVersion(
	t *testing.T,
	endpoint string,
	version int,
) spec.Package {
	t.Helper()
	inputSchema := map[string]any{
		"$schema": "https://json-schema.org/draft/2020-12/schema",
		"type":    "object", "additionalProperties": false,
		"required": []any{"owner", "title", "metadata"},
		"properties": map[string]any{
			"owner":    map[string]any{"type": "string"},
			"title":    map[string]any{"type": "string"},
			"labels":   map[string]any{"type": "array", "items": map[string]any{"type": "string"}},
			"metadata": map[string]any{"type": "object"},
		},
	}
	outputSchema := map[string]any{
		"$schema": "https://json-schema.org/draft/2020-12/schema",
		"type":    "object", "additionalProperties": true,
	}
	pkg := spec.Package{
		Metadata: spec.Metadata{
			SchemaVersion: 1, Domain: "e2e-provider", Version: version,
			Name: "E2E provider", Endpoint: spec.Endpoint{BaseURL: endpoint + "/v1"},
			Auth: spec.Auth{
				Default: "default",
				Profiles: []spec.AuthProfile{{
					ID: "default", Type: "oauth2_bearer",
				}},
			},
		},
		Actions: []spec.Action{{
			Name: "create-item", File: "actions/create-item.yaml",
			Auth: &spec.ActionAuthRequirement{Profile: "default"},
			Catalog: spec.Catalog{
				Summary: "Create item", Description: "Create one deterministic item.",
				Safety:      spec.Safety{Class: "mutation"},
				InputSchema: inputSchema, OutputSchema: outputSchema,
				Example: map[string]any{
					"owner": "pingcap", "title": "hello",
					"metadata": map[string]any{"priority": "p0"},
				},
			},
			Binding: spec.Binding{
				Method: http.MethodPost, Path: "/repos/{owner}/items",
				Parameters: []spec.Parameter{
					{From: "/owner", In: "path", Name: "owner", Style: "simple"},
					{From: "/labels", In: "query", Name: "labels", Style: "form", Explode: true},
				},
				Body: &spec.Body{
					ContentType: "application/json",
					Mappings: []spec.BodyMapping{
						{From: "/title", To: "/title"},
						{From: "/metadata", To: "/metadata"},
					},
				},
				Response: spec.Response{
					Mode: "passthrough", SuccessStatuses: []int{http.StatusOK},
					ContentTypes: []string{"application/json"},
				},
			},
		}},
	}
	digest, err := spec.DefinitionDigest(pkg)
	require.NoError(t, err)
	pkg.Metadata.Digest = digest
	pkg.Digest = digest
	return pkg
}

func seedExtensionE2E(
	t *testing.T,
	db coredb.QueryExecer,
	now time.Time,
) {
	t.Helper()
	mustExec(t, db, `
INSERT INTO users (id, email, name, display_name, password_hash, created_at, updated_at)
VALUES ('usr_extension_full_e2e', 'extension-full-e2e@example.com',
        'Extension Full E2E', 'Extension Full E2E', 'hash', $1, $1)`,
		now)
	mustExec(t, db, `
INSERT INTO organizations (id, slug, name, created_by_user_id, created_at, updated_at)
VALUES ('org_extension_full_e2e', 'extension-full-e2e', 'Extension Full E2E',
        'usr_extension_full_e2e', $1, $1)`,
		now)
	mustExec(t, db, `
INSERT INTO workspaces (id, organization_id, slug, name, created_at, updated_at)
VALUES ($1, 'org_extension_full_e2e', 'extension-full-e2e',
        'Extension Full E2E', $2, $2)`,
		e2eWorkspace, now)
	mustExec(t, db, `
INSERT INTO members (
    id, workspace_id, type, display_name, user_id, workspace_role,
    status, created_at, updated_at
) VALUES (
    'mem_owner_extension_full_e2e', $1, 'human', 'Owner',
    'usr_extension_full_e2e', 'owner', 'active', $2, $2
)`, e2eWorkspace, now)
	mustExec(t, db, `
INSERT INTO members (
    id, workspace_id, type, display_name, status, created_at, updated_at
) VALUES ($1, $2, 'agent', 'Extension Agent', 'active', $3, $3)`,
		e2eMember, e2eWorkspace, now)
	mustExec(t, db, `
INSERT INTO machines (
    id, workspace_id, created_by_user_id, name, api_key, status,
    connected_at, disconnected_at, created_at, updated_at
) VALUES (
    $1, $2, 'usr_extension_full_e2e', 'Extension machine',
    'machine-api-key', 'active', $3, NULL, $3, $3
)`, e2eMachine, e2eWorkspace, now)
	mustExec(t, db, `
INSERT INTO agents (
    id, workspace_id, machine_id, member_id, name, runtime, model,
    description, status, activity, activity_detail, created_at, updated_at
) VALUES (
    $1, $2, $3, $4, 'Extension Agent', 'codex', 'gpt',
    '', 'active', 'active', '', $5, $5
)`, e2eAgent, e2eWorkspace, e2eMachine, e2eMember, now)
}

func mustExec(
	t *testing.T,
	db coredb.QueryExecer,
	query string,
	args ...any,
) {
	t.Helper()
	_, err := db.Exec(context.Background(), query, args...)
	require.NoError(t, err)
}
