package serviceaccount

import (
	"context"
	"crypto/sha256"
	"encoding/hex"
	"fmt"
	"sync"
	"testing"
	"time"

	"github.com/stretchr/testify/require"

	"github.com/tidbcloud/AgentTeam/internal/coredb"
	"github.com/tidbcloud/AgentTeam/internal/coredb/testkit"
)

func TestCreateRequestAuthoritativeStateAndReferenceInvariants(t *testing.T) {
	ctx := context.Background()
	db := startServiceAccountRollbackDB(t, ctx)
	now := time.Date(2026, 7, 10, 11, 0, 0, 0, time.UTC)
	fixture := seedServiceAccountFixture(t, db, "request_invariants", "request-invariants", "sacct_request_invariants", now)
	other := seedServiceAccountFixture(t, db, "request_other", "request-other", "sacct_request_other", now)
	store := New(db)

	_, err := insertInvariantRequest(ctx, db, store, fixture, "sacreq_caller_state", "caller-state", AuthorizationPathApprovalRequired, RequestStateAuthorized, now)
	require.ErrorIs(t, err, ErrBadRequest)
	require.Equal(t, int64(0), invariantCount(t, ctx, db, `SELECT COUNT(*) FROM service_account_create_requests WHERE id = 'sacreq_caller_state'`))

	request, err := insertInvariantRequest(ctx, db, store, fixture, "sacreq_authoritative", "authoritative", AuthorizationPathApprovalRequired, "", now)
	require.NoError(t, err)
	require.Equal(t, RequestStateAwaitingAuthorization, request.BusinessState)
	require.EqualValues(t, 1, request.Version)

	_, err = insertInvariantRequest(ctx, db, store, fixture, request.ID, "different-client", AuthorizationPathApprovalRequired, "", now)
	require.ErrorIs(t, err, ErrConflict)
	_, err = insertInvariantRequest(ctx, db, store, fixture, "sacreq_different_path", "authoritative", AuthorizationPathDirectCreate, "", now)
	require.ErrorIs(t, err, ErrConflict)

	require.ErrorIs(t, transitionInvariantRequest(ctx, db, store, fixture.WorkspaceID, request.ID, request.Version, RequestStateAuthorized, "", 0, "", now.Add(time.Second)), ErrBadRequest)

	request = requireTransitionInvariantRequest(t, ctx, db, store, fixture.WorkspaceID, request.ID, request.Version, RequestStateAwaitingAuthorization, "approval_request", 0, "", now.Add(2*time.Second))
	require.EqualValues(t, 2, request.Version)
	require.Equal(t, "approval_request", request.ApprovalInstanceID)

	require.ErrorIs(t, transitionInvariantRequest(ctx, db, store, fixture.WorkspaceID, request.ID, request.Version, RequestStateAuthorized, "approval_other", 1, "", now.Add(3*time.Second)), ErrConflict)
	require.ErrorIs(t, transitionInvariantRequest(ctx, db, store, fixture.WorkspaceID, request.ID, request.Version, RequestStateAuthorized, "approval_request", 0, "", now.Add(3*time.Second)), ErrBadRequest)

	request = requireTransitionInvariantRequest(t, ctx, db, store, fixture.WorkspaceID, request.ID, request.Version, RequestStateAuthorized, "approval_request", 1, "", now.Add(4*time.Second))
	require.EqualValues(t, 1, request.ConsumedDecisionVersion)
	require.EqualValues(t, 3, request.Version)

	require.ErrorIs(t, transitionInvariantRequest(ctx, db, store, fixture.WorkspaceID, request.ID, request.Version, RequestStateProvisioning, "", 0, "", now.Add(5*time.Second)), ErrBadRequest)

	otherAccount := insertFixtureAccount(t, ctx, db, store, other, "request-other", "sacct_request_other", "", now)
	require.ErrorIs(t, transitionInvariantRequest(ctx, db, store, fixture.WorkspaceID, request.ID, request.Version, RequestStateProvisioning, "", 0, otherAccount.ID, now.Add(5*time.Second)), ErrConflict)
	account := insertFixtureAccount(t, ctx, db, store, fixture, "request-invariants", "sacct_request_invariants", "", now)
	require.ErrorIs(t, transitionInvariantRequest(ctx, db, store, fixture.WorkspaceID, request.ID, request.Version, RequestStateProvisioning, "", 2, account.ID, now.Add(5*time.Second)), ErrBadRequest)

	request = requireTransitionInvariantRequest(t, ctx, db, store, fixture.WorkspaceID, request.ID, request.Version, RequestStateProvisioning, "", 0, account.ID, now.Add(6*time.Second))
	require.Equal(t, account.ID, request.CreatedServiceAccountID)
	require.EqualValues(t, 4, request.Version)

	request = requireTransitionInvariantRequest(t, ctx, db, store, fixture.WorkspaceID, request.ID, request.Version, RequestStateFulfilled, "", 0, "", now.Add(7*time.Second))
	require.Equal(t, RequestStateFulfilled, request.BusinessState)
	require.NotNil(t, request.FulfilledAt)
	require.Equal(t, account.ID, request.CreatedServiceAccountID)
	require.ErrorIs(t, transitionInvariantRequest(ctx, db, store, fixture.WorkspaceID, request.ID, request.Version, RequestStateActionRequired, "", 0, "", now.Add(8*time.Second)), ErrConflict)

	declined, err := insertInvariantRequest(ctx, db, store, fixture, "sacreq_declined", "declined", AuthorizationPathApprovalRequired, "", now)
	require.NoError(t, err)
	declined = requireTransitionInvariantRequest(t, ctx, db, store, fixture.WorkspaceID, declined.ID, declined.Version, RequestStateAwaitingAuthorization, "approval_declined", 0, "", now.Add(time.Second))
	declined = requireTransitionInvariantRequest(t, ctx, db, store, fixture.WorkspaceID, declined.ID, declined.Version, RequestStateDeclined, "approval_declined", 1, "", now.Add(2*time.Second))
	require.ErrorIs(t, transitionInvariantRequest(ctx, db, store, fixture.WorkspaceID, declined.ID, declined.Version, RequestStateAuthorized, "approval_declined", 2, "", now.Add(3*time.Second)), ErrConflict)

	cancelled, err := insertInvariantRequest(ctx, db, store, fixture, "sacreq_cancelled", "cancelled", AuthorizationPathApprovalRequired, "", now)
	require.NoError(t, err)
	cancelled = requireTransitionInvariantRequest(t, ctx, db, store, fixture.WorkspaceID, cancelled.ID, cancelled.Version, RequestStateCancelled, "", 0, "", now.Add(time.Second))
	require.NotNil(t, cancelled.CancelledAt)
	require.ErrorIs(t, transitionInvariantRequest(ctx, db, store, fixture.WorkspaceID, cancelled.ID, cancelled.Version, RequestStateAwaitingAuthorization, "approval_cancelled", 0, "", now.Add(2*time.Second)), ErrConflict)

	direct, err := insertInvariantRequest(ctx, db, store, fixture, "sacreq_direct", "direct", AuthorizationPathDirectCreate, "", now)
	require.NoError(t, err)
	require.Equal(t, RequestStateAuthorized, direct.BusinessState)
	require.ErrorIs(t, transitionInvariantRequest(ctx, db, store, fixture.WorkspaceID, direct.ID, direct.Version, RequestStateProvisioning, "approval_forbidden", 1, account.ID, now.Add(time.Second)), ErrBadRequest)
}

func TestCreateRequestApprovalInstanceIsOneToOne(t *testing.T) {
	ctx := context.Background()
	db := startServiceAccountRollbackDB(t, ctx)
	now := time.Date(2026, 7, 10, 11, 30, 0, 0, time.UTC)
	fixture := seedServiceAccountFixture(t, db, "request_approval_one_to_one", "request-approval-one-to-one", "sacct_request_approval_one_to_one", now)
	store := New(db)

	first, err := insertInvariantRequest(ctx, db, store, fixture, "sacreq_approval_first", "approval-first", AuthorizationPathApprovalRequired, "", now)
	require.NoError(t, err)
	second, err := insertInvariantRequest(ctx, db, store, fixture, "sacreq_approval_second", "approval-second", AuthorizationPathApprovalRequired, "", now)
	require.NoError(t, err)

	first = requireTransitionInvariantRequest(t, ctx, db, store, fixture.WorkspaceID, first.ID, first.Version, RequestStateAwaitingAuthorization, "approval_shared", 0, "", now.Add(time.Second))
	require.Equal(t, "approval_shared", first.ApprovalInstanceID)

	require.ErrorIs(t, transitionInvariantRequest(ctx, db, store, fixture.WorkspaceID, second.ID, second.Version, RequestStateAwaitingAuthorization, "approval_shared", 0, "", now.Add(2*time.Second)), ErrConflict)
	unchanged, err := store.LoadCreateRequest(ctx, fixture.WorkspaceID, second.ID)
	require.NoError(t, err)
	require.Empty(t, unchanged.ApprovalInstanceID)
	require.Equal(t, second.Version, unchanged.Version)

	second = requireTransitionInvariantRequest(t, ctx, db, store, fixture.WorkspaceID, second.ID, second.Version, RequestStateAwaitingAuthorization, "approval_second", 0, "", now.Add(3*time.Second))
	require.Equal(t, "approval_second", second.ApprovalInstanceID)
}

func TestAllMembersAreActiveHumansRequiresEveryDistinctMember(t *testing.T) {
	ctx := context.Background()
	db := startServiceAccountRollbackDB(t, ctx)
	now := time.Date(2026, 7, 10, 11, 45, 0, 0, time.UTC)
	fixture := seedServiceAccountFixture(t, db, "active_humans", "active-humans", "sacct_active_humans", now)
	activePeerID := seedInvariantHuman(t, ctx, db, fixture, "active_peer", now)
	inactivePeerID := seedInvariantHuman(t, ctx, db, fixture, "inactive_peer", now)
	mustExec(t, db, `
UPDATE members
SET status = 'inactive', updated_at = $2
WHERE id = $1
`, inactivePeerID, now.Add(time.Second))

	require.NoError(t, coredb.WithTx(ctx, db, func(tx coredb.Tx) error {
		store := New(db).InTx(tx)
		allActive, err := store.AllMembersAreActiveHumans(ctx, fixture.WorkspaceID, []string{
			fixture.HumanMemberID,
			activePeerID,
			activePeerID,
		})
		require.NoError(t, err)
		require.True(t, allActive)

		allActive, err = store.AllMembersAreActiveHumans(ctx, fixture.WorkspaceID, []string{fixture.HumanMemberID, inactivePeerID})
		require.NoError(t, err)
		require.False(t, allActive)

		allActive, err = store.AllMembersAreActiveHumans(ctx, fixture.WorkspaceID, nil)
		require.NoError(t, err)
		require.True(t, allActive)
		return nil
	}))
}

func TestLoadCreatedAuditMetadataReturnsEarliestCreationReceipt(t *testing.T) {
	ctx := context.Background()
	db := startServiceAccountRollbackDB(t, ctx)
	now := time.Date(2026, 7, 10, 11, 50, 0, 0, time.UTC)
	fixture := seedServiceAccountFixture(t, db, "created_audit", "created-audit", "sacct_created_audit", now)
	store := New(db)
	account := insertFixtureAccount(t, ctx, db, store, fixture, "created-audit", "sacct_created_audit", "", now)

	require.NoError(t, coredb.WithTx(ctx, db, func(tx coredb.Tx) error {
		txStore := store.InTx(tx)
		for _, event := range []AuditEvent{
			{
				ID: "saaudit_created_second", WorkspaceID: fixture.WorkspaceID, ServiceAccountID: account.ID,
				EventType: "service_account.created", SubjectType: "service_account", SubjectID: account.ID,
				ActorType: "service", ActorID: account.PrincipalID, Metadata: []byte(`{"receipt":"second"}`), CreatedAt: now.Add(time.Second),
			},
			{
				ID: "saaudit_created_first", WorkspaceID: fixture.WorkspaceID, ServiceAccountID: account.ID,
				EventType: "service_account.created", SubjectType: "service_account", SubjectID: account.ID,
				ActorType: "service", ActorID: account.PrincipalID, Metadata: []byte(`{"receipt":"first"}`), CreatedAt: now,
			},
		} {
			if err := txStore.InsertAuditEvent(ctx, event); err != nil {
				return err
			}
		}
		metadata, err := txStore.LoadCreatedAuditMetadata(ctx, fixture.WorkspaceID, account.ID)
		require.NoError(t, err)
		require.JSONEq(t, `{"receipt":"first"}`, string(metadata))
		return nil
	}))
}

func TestWorkspaceManagerNaturalKeyActorAndLastManagerInvariants(t *testing.T) {
	ctx := context.Background()
	db := startServiceAccountRollbackDB(t, ctx)
	now := time.Date(2026, 7, 10, 12, 0, 0, 0, time.UTC)
	fixture := seedServiceAccountFixture(t, db, "manager_invariants", "manager-invariants", "sacct_manager_invariants", now)
	secondMemberID := seedInvariantHuman(t, ctx, db, fixture, "second", now)
	store := New(db)
	account := insertFixtureAccount(t, ctx, db, store, fixture, "manager-invariants", "sacct_manager_invariants", "", now)

	require.ErrorIs(t, upsertInvariantManager(ctx, db, store, ManagerUpsert{
		ID: "samanager_bad_human_actor", WorkspaceID: fixture.WorkspaceID,
		ServiceAccountID: account.ID, MemberID: secondMemberID,
		GrantedByMemberID: fixture.HumanMemberID, GrantedByActorType: "human",
		GrantedByActorID: secondMemberID, Now: now,
	}), ErrBadRequest)

	managerOne := requireUpsertInvariantManager(t, ctx, db, store, ManagerUpsert{
		ID: "samanager_one", WorkspaceID: fixture.WorkspaceID,
		ServiceAccountID: account.ID, MemberID: fixture.HumanMemberID,
		GrantedByMemberID: fixture.HumanMemberID, GrantedByActorType: "human",
		GrantedByActorID: fixture.HumanMemberID, Now: now,
	})
	managerTwo := requireUpsertInvariantManager(t, ctx, db, store, ManagerUpsert{
		ID: "samanager_two", WorkspaceID: fixture.WorkspaceID,
		ServiceAccountID: account.ID, MemberID: secondMemberID,
		GrantedByMemberID: fixture.HumanMemberID, GrantedByActorType: "human",
		GrantedByActorID: fixture.HumanMemberID, Now: now,
	})

	replayed := requireUpsertInvariantManager(t, ctx, db, store, ManagerUpsert{
		ID: "samanager_one_replay", WorkspaceID: fixture.WorkspaceID,
		ServiceAccountID: account.ID, MemberID: fixture.HumanMemberID,
		GrantedByMemberID: secondMemberID, GrantedByActorType: "human",
		GrantedByActorID: secondMemberID, Now: now.Add(time.Second),
	})
	require.Equal(t, managerOne.ID, replayed.ID)
	require.EqualValues(t, managerOne.Version, replayed.Version)
	require.Equal(t, managerOne.GrantedByMemberID, replayed.GrantedByMemberID)

	ended := requireEndInvariantManager(t, ctx, db, store, fixture.WorkspaceID, account.ID, managerOne.ID, managerOne.Version, now.Add(2*time.Second))
	require.Equal(t, ManagerStatusEnded, ended.Status)
	idempotent := requireEndInvariantManager(t, ctx, db, store, fixture.WorkspaceID, account.ID, managerOne.ID, managerOne.Version, now.Add(3*time.Second))
	require.Equal(t, ended.Version, idempotent.Version)
	require.ErrorIs(t, endInvariantManager(ctx, db, store, fixture.WorkspaceID, account.ID, managerTwo.ID, managerTwo.Version, now.Add(3*time.Second)), ErrConflict)

	regranted := requireUpsertInvariantManager(t, ctx, db, store, ManagerUpsert{
		ID: "samanager_one_regrant", WorkspaceID: fixture.WorkspaceID,
		ServiceAccountID: account.ID, MemberID: fixture.HumanMemberID,
		GrantedByMemberID: secondMemberID, GrantedByActorType: "human",
		GrantedByActorID: secondMemberID, Now: now.Add(4 * time.Second),
	})
	require.Equal(t, managerOne.ID, regranted.ID)
	require.EqualValues(t, ended.Version+1, regranted.Version)
	require.Equal(t, secondMemberID, regranted.GrantedByMemberID)
	require.Nil(t, regranted.EndedAt)

	platformFixture := seedServiceAccountFixture(t, db, "manager_platform", "Manager Platform", "psacct_manager_platform", now)
	var platform PlatformAccount
	require.NoError(t, coredb.WithTx(ctx, db, func(tx coredb.Tx) error {
		txStore := store.InTx(tx)
		if _, err := txStore.LockNameNamespaceAt(ctx, "Manager Platform", now); err != nil {
			return err
		}
		var err error
		platform, err = txStore.InsertPlatformAccount(ctx, PlatformAccount{
			ID: "psacct_manager_platform", Name: "Manager Platform",
			CreatedByOperatorUserID: platformFixture.UserID, CreatedAt: now, UpdatedAt: now,
		})
		return err
	}))
	mustExec(t, db, `
UPDATE service_installations
SET created_by_member_id = NULL, created_by_actor_type = 'platform_operator',
    created_by_actor_id = $2, updated_at = $3
WHERE id = $1
`, platformFixture.ServiceInstallationID, platformFixture.UserID, now)
	platformLocal := insertFixtureAccount(t, ctx, db, store, platformFixture, "Manager Platform", "sacct_manager_platform_local", platform.ID, now)
	require.ErrorIs(t, upsertInvariantManager(ctx, db, store, ManagerUpsert{
		ID: "samanager_platform_forbidden", WorkspaceID: platformFixture.WorkspaceID,
		ServiceAccountID: platformLocal.ID, MemberID: platformFixture.HumanMemberID,
		GrantedByMemberID: platformFixture.HumanMemberID, GrantedByActorType: "human",
		GrantedByActorID: platformFixture.HumanMemberID, Now: now,
	}), ErrConflict)
}

func TestConcurrentLastManagerEndSerializesOnAccountInvariant(t *testing.T) {
	ctx := context.Background()
	dsn := testkit.StartInitializedTiDBWithRootDSN(t, serviceAccountTiDB.RootDSN(t))
	sqlDB := testkit.OpenTiDB(t, dsn)
	sqlDB.SetMaxOpenConns(4)
	db := coredb.WrapSQLDB(sqlDB)
	now := time.Date(2026, 7, 10, 13, 0, 0, 0, time.UTC)
	fixture := seedServiceAccountFixture(t, db, "manager_concurrent", "manager-concurrent", "sacct_manager_concurrent", now)
	secondMemberID := seedInvariantHuman(t, ctx, db, fixture, "concurrent", now)
	store := New(db)
	account := insertFixtureAccount(t, ctx, db, store, fixture, "manager-concurrent", "sacct_manager_concurrent", "", now)

	managers := []ManagerAssignment{
		requireUpsertInvariantManager(t, ctx, db, store, ManagerUpsert{
			ID: "samanager_concurrent_one", WorkspaceID: fixture.WorkspaceID,
			ServiceAccountID: account.ID, MemberID: fixture.HumanMemberID,
			GrantedByMemberID: fixture.HumanMemberID, GrantedByActorType: "human",
			GrantedByActorID: fixture.HumanMemberID, Now: now,
		}),
		requireUpsertInvariantManager(t, ctx, db, store, ManagerUpsert{
			ID: "samanager_concurrent_two", WorkspaceID: fixture.WorkspaceID,
			ServiceAccountID: account.ID, MemberID: secondMemberID,
			GrantedByMemberID: fixture.HumanMemberID, GrantedByActorType: "human",
			GrantedByActorID: fixture.HumanMemberID, Now: now,
		}),
	}

	start := make(chan struct{})
	errorsByManager := make(chan error, len(managers))
	var wait sync.WaitGroup
	for _, manager := range managers {
		manager := manager
		wait.Add(1)
		go func() {
			defer wait.Done()
			<-start
			errorsByManager <- endInvariantManager(ctx, db, store, fixture.WorkspaceID, account.ID, manager.ID, manager.Version, now.Add(time.Second))
		}()
	}
	close(start)
	wait.Wait()
	close(errorsByManager)

	succeeded, conflicted := 0, 0
	for err := range errorsByManager {
		if err == nil {
			succeeded++
			continue
		}
		require.ErrorIs(t, err, ErrConflict)
		conflicted++
	}
	require.Equal(t, 1, succeeded)
	require.Equal(t, 1, conflicted)
	require.Equal(t, int64(1), invariantCount(t, ctx, db, `
SELECT COUNT(*) FROM service_account_managers
WHERE service_account_id = $1 AND status = 'active' AND ended_at IS NULL
`, account.ID))
}

func insertInvariantRequest(ctx context.Context, db coredb.DB, store *Store, fixture serviceAccountFixture, requestID string, clientID string, path string, state string, now time.Time) (CreateRequest, error) {
	payload := []byte(fmt.Sprintf(`{"client":%q}`, clientID))
	digest := sha256.Sum256(payload)
	var request CreateRequest
	err := coredb.WithTx(ctx, db, func(tx coredb.Tx) error {
		var err error
		request, err = store.InTx(tx).InsertCreateRequest(ctx, CreateRequestInsert{
			ID:                  requestID,
			WorkspaceID:         fixture.WorkspaceID,
			RequestedByMemberID: fixture.HumanMemberID,
			ClientRequestID:     clientID,
			PayloadSnapshot:     payload,
			PayloadDigest:       hex.EncodeToString(digest[:]),
			AuthorizationPath:   path,
			BusinessState:       state,
			Now:                 now,
		})
		return err
	})
	return request, err
}

func transitionInvariantRequest(ctx context.Context, db coredb.DB, store *Store, workspaceID string, requestID string, expectedVersion int64, state string, approvalID string, decisionVersion int64, accountID string, now time.Time) error {
	return coredb.WithTx(ctx, db, func(tx coredb.Tx) error {
		_, err := store.InTx(tx).TransitionCreateRequest(ctx, workspaceID, requestID, expectedVersion, state, approvalID, decisionVersion, accountID, now)
		return err
	})
}

func requireTransitionInvariantRequest(t testing.TB, ctx context.Context, db coredb.DB, store *Store, workspaceID string, requestID string, expectedVersion int64, state string, approvalID string, decisionVersion int64, accountID string, now time.Time) CreateRequest {
	t.Helper()
	var request CreateRequest
	require.NoError(t, coredb.WithTx(ctx, db, func(tx coredb.Tx) error {
		var err error
		request, err = store.InTx(tx).TransitionCreateRequest(ctx, workspaceID, requestID, expectedVersion, state, approvalID, decisionVersion, accountID, now)
		return err
	}))
	return request
}

func seedInvariantHuman(t testing.TB, ctx context.Context, db coredb.QueryExecer, fixture serviceAccountFixture, suffix string, now time.Time) string {
	t.Helper()
	userID := "usr_sa_" + suffix + "_" + fixture.WorkspaceID
	memberID := "mem_sa_" + suffix + "_" + fixture.WorkspaceID
	mustExec(t, db, `
INSERT INTO users (id, email, name, display_name, password_hash, created_at, updated_at)
VALUES ($1, $2, $3, $3, 'hash', $4, $4)
`, userID, userID+"@example.com", suffix, now)
	mustExec(t, db, `
INSERT INTO members (id, workspace_id, type, display_name, user_id, workspace_role, status, created_at, updated_at)
VALUES ($1, $2, 'human', $3, $4, 'member', 'active', $5, $5)
`, memberID, fixture.WorkspaceID, suffix, userID, now)
	return memberID
}

func upsertInvariantManager(ctx context.Context, db coredb.DB, store *Store, params ManagerUpsert) error {
	return coredb.WithTx(ctx, db, func(tx coredb.Tx) error {
		_, err := store.InTx(tx).UpsertManager(ctx, params)
		return err
	})
}

func requireUpsertInvariantManager(t testing.TB, ctx context.Context, db coredb.DB, store *Store, params ManagerUpsert) ManagerAssignment {
	t.Helper()
	var manager ManagerAssignment
	require.NoError(t, coredb.WithTx(ctx, db, func(tx coredb.Tx) error {
		var err error
		manager, err = store.InTx(tx).UpsertManager(ctx, params)
		return err
	}))
	return manager
}

func endInvariantManager(ctx context.Context, db coredb.DB, store *Store, workspaceID string, accountID string, managerID string, expectedVersion int64, now time.Time) error {
	return coredb.WithTx(ctx, db, func(tx coredb.Tx) error {
		_, err := store.InTx(tx).EndManager(ctx, workspaceID, accountID, managerID, expectedVersion, now)
		return err
	})
}

func requireEndInvariantManager(t testing.TB, ctx context.Context, db coredb.DB, store *Store, workspaceID string, accountID string, managerID string, expectedVersion int64, now time.Time) ManagerAssignment {
	t.Helper()
	var manager ManagerAssignment
	require.NoError(t, coredb.WithTx(ctx, db, func(tx coredb.Tx) error {
		var err error
		manager, err = store.InTx(tx).EndManager(ctx, workspaceID, accountID, managerID, expectedVersion, now)
		return err
	}))
	return manager
}

func invariantCount(t testing.TB, ctx context.Context, queryer coredb.QueryRower, query string, args ...any) int64 {
	t.Helper()
	var count int64
	require.NoError(t, queryer.QueryRow(ctx, query, args...).Scan(&count))
	return count
}
