package app

import (
	"context"
	"errors"
	"fmt"
	"strings"

	"github.com/tidbcloud/AgentTeam/internal/core/domain"
	"github.com/tidbcloud/AgentTeam/internal/coredb"
	"github.com/tidbcloud/AgentTeam/internal/store/access"
	"github.com/tidbcloud/AgentTeam/internal/store/message"
)

type humanMessageTargetQueryer interface {
	QueryRow(context.Context, string, ...any) coredb.Row
	Query(context.Context, string, ...any) (coredb.Rows, error)
}

var (
	workspaceAccessAction = access.HumanAction{
		Resource: access.ResourceWorkspace,
		Action:   access.ActionAccess,
	}
	channelReadAction = access.HumanAction{
		Resource: access.ResourceChannel,
		Action:   access.ActionRead,
	}
	channelPostAction = access.HumanAction{
		Resource: access.ResourceChannel,
		Action:   access.ActionPost,
	}
	channelInviteMembersAction = access.HumanAction{
		Resource: access.ResourceChannel,
		Action:   access.ActionInviteMembers,
	}
	channelManageMembersAction = access.HumanAction{
		Resource: access.ResourceChannel,
		Action:   access.ActionManageMembers,
	}
	dmReadAction = access.HumanAction{
		Resource: access.ResourceDM,
		Action:   access.ActionRead,
	}
	dmPostAction = access.HumanAction{
		Resource: access.ResourceDM,
		Action:   access.ActionPost,
	}
	threadReadAction = access.HumanAction{
		Resource: access.ResourceThread,
		Action:   access.ActionRead,
	}
	threadPostAction = access.HumanAction{
		Resource: access.ResourceThread,
		Action:   access.ActionPost,
	}
	workspaceManageMembersAction = access.HumanAction{
		Resource: access.ResourceWorkspace,
		Action:   access.ActionManageMembers,
	}
	workspaceManageMemberRolesAction = access.HumanAction{
		Resource: access.ResourceWorkspace,
		Action:   access.ActionManageMemberRoles,
	}
	workspaceManageRuntimeAction = access.HumanAction{
		Resource: access.ResourceWorkspace,
		Action:   access.ActionManageRuntime,
	}
	workspaceInstallServiceAction = access.HumanAction{
		Resource: access.ResourceWorkspace,
		Action:   access.ActionInstallService,
	}
	serviceInstallationApproveAction = access.HumanAction{
		Resource: access.ResourceServiceInstallation,
		Action:   access.ActionApprove,
	}
)

func (store *Store) loadAuthorizedHumanWorkspaceMember(ctx context.Context, queryer interface {
	QueryRow(context.Context, string, ...any) coredb.Row
}, userID string, workspaceID string, action access.HumanAction) (access.HumanWorkspaceMember, error) {
	member, err := access.LoadAuthorizedHumanWorkspaceMember(ctx, queryer, userID, workspaceID, action)
	if errors.Is(err, access.ErrForbidden) {
		return access.HumanWorkspaceMember{}, ErrForbidden
	}
	if err != nil {
		return access.HumanWorkspaceMember{}, fmt.Errorf("load authorized human workspace member: %w", err)
	}
	return member, nil
}

func (store *Store) loadAuthorizedHumanWorkspaceMemberForUpdate(ctx context.Context, queryer interface {
	QueryRow(context.Context, string, ...any) coredb.Row
}, userID string, workspaceID string, action access.HumanAction) (access.HumanWorkspaceMember, error) {
	member, err := access.LoadAuthorizedHumanWorkspaceMemberForUpdate(ctx, queryer, userID, workspaceID, action)
	if errors.Is(err, access.ErrForbidden) {
		return access.HumanWorkspaceMember{}, ErrForbidden
	}
	if err != nil {
		return access.HumanWorkspaceMember{}, fmt.Errorf("load authorized human workspace member for update: %w", err)
	}
	return member, nil
}

func (store *Store) loadAuthorizedHumanChannelAccess(ctx context.Context, queryer interface {
	QueryRow(context.Context, string, ...any) coredb.Row
}, userID string, workspaceID string, channelID string, action access.HumanAction) (access.HumanChannelAccess, error) {
	channelAccess, err := access.LoadHumanChannelAccess(ctx, queryer, userID, workspaceID, channelID)
	if errors.Is(err, access.ErrForbidden) {
		return access.HumanChannelAccess{}, ErrForbidden
	}
	if errors.Is(err, access.ErrNotFound) {
		return access.HumanChannelAccess{}, ErrNotFound
	}
	if err != nil {
		return access.HumanChannelAccess{}, fmt.Errorf("load human channel access: %w", err)
	}

	if channelAccess.Channel.Slug != "all" {
		if channelAccess.Channel.OwnerMemberID == nil && strings.TrimSpace(channelAccess.Channel.TeamID) == "" {
			return access.HumanChannelAccess{}, ErrNotFound
		}
		if channelAccess.Channel.Visibility == "private" && !channelAccess.Joined {
			return access.HumanChannelAccess{}, ErrNotFound
		}
	}

	request := access.NewHumanAuthorizationRequest(
		userID,
		workspaceID,
		channelAccess.WorkspaceMember.Member.ID,
		channelID,
		action,
	)
	if _, err := access.DecideHumanChannelAuthorization(channelAccess, request); err != nil {
		if errors.Is(err, access.ErrForbidden) {
			return access.HumanChannelAccess{}, ErrForbidden
		}
		return access.HumanChannelAccess{}, fmt.Errorf("authorize human channel access: %w", err)
	}
	channelAccess.Channel.ThreadID = channelAccess.Channel.ChannelID
	return channelAccess, nil
}

func (store *Store) requireWorkspaceAccess(ctx context.Context, userID string, workspaceID string) (access.HumanWorkspaceMember, error) {
	return store.loadAuthorizedHumanWorkspaceMember(ctx, store.db, userID, workspaceID, workspaceAccessAction)
}

func (store *Store) requireChannelMembershipManagement(ctx context.Context, userID string, workspaceID string, channelID string) (access.HumanChannelAccess, error) {
	return store.loadAuthorizedHumanChannelAccess(ctx, store.db, userID, workspaceID, channelID, channelManageMembersAction)
}

func (store *Store) requireChannelInvite(ctx context.Context, userID string, workspaceID string, channelID string) (access.HumanChannelAccess, error) {
	return store.loadAuthorizedHumanChannelAccess(ctx, store.db, userID, workspaceID, channelID, channelInviteMembersAction)
}

func (store *Store) requireWorkspaceMemberManagement(ctx context.Context, userID string, workspaceID string) error {
	_, err := store.loadAuthorizedHumanWorkspaceMember(ctx, store.db, userID, workspaceID, workspaceManageMembersAction)
	return err
}

// RequireWorkspaceMemberManagementForUser checks whether a user can manage
// human membership in the workspace.
func (store *Store) RequireWorkspaceMemberManagementForUser(ctx context.Context, userID string, workspaceID string) error {
	return store.requireWorkspaceMemberManagement(ctx, userID, workspaceID)
}

func (store *Store) requireWorkspaceAdmin(ctx context.Context, userID string, workspaceID string) (access.HumanWorkspaceMember, error) {
	member, err := store.loadRuntimeWorkspaceMember(ctx, store.db, userID, workspaceID)
	if err != nil {
		return access.HumanWorkspaceMember{}, err
	}
	if !access.HasWorkspaceAdminRole(domain.WorkspaceRole(member.WorkspaceRole)) {
		return access.HumanWorkspaceMember{}, ErrForbidden
	}
	return member, nil
}

// RequireWorkspaceAppManagementForUser is the generic authorization seam used
// by feature-owned App setup flows before external provider I/O.
func (store *Store) RequireWorkspaceAppManagementForUser(
	ctx context.Context,
	userID string,
	workspaceID string,
) error {
	_, err := store.requireWorkspaceAdmin(ctx, userID, workspaceID)
	return err
}

func (store *Store) requireWorkspaceMemberRoleManagement(ctx context.Context, userID string, workspaceID string) error {
	_, err := store.loadAuthorizedHumanWorkspaceMember(ctx, store.db, userID, workspaceID, workspaceManageMemberRolesAction)
	return err
}

func (store *Store) requireWorkspaceRuntimeManagement(ctx context.Context, userID string, workspaceID string) error {
	_, err := store.loadAuthorizedHumanWorkspaceMember(ctx, store.db, userID, workspaceID, workspaceManageRuntimeAction)
	return err
}

func (store *Store) loadRuntimeWorkspaceMember(ctx context.Context, queryer interface {
	QueryRow(context.Context, string, ...any) coredb.Row
}, userID string, workspaceID string) (access.HumanWorkspaceMember, error) {
	return store.loadAuthorizedHumanWorkspaceMember(ctx, queryer, userID, workspaceID, workspaceAccessAction)
}

func (store *Store) loadRuntimeWorkspaceMemberForUpdate(ctx context.Context, queryer interface {
	QueryRow(context.Context, string, ...any) coredb.Row
}, userID string, workspaceID string) (access.HumanWorkspaceMember, error) {
	return store.loadAuthorizedHumanWorkspaceMemberForUpdate(ctx, queryer, userID, workspaceID, workspaceAccessAction)
}

func mapAccessOwnershipError(err error) error {
	if errors.Is(err, access.ErrNotFound) || errors.Is(err, ErrNotFound) {
		return ErrNotFound
	}
	return err
}

func machineCreatedByUser(ctx context.Context, queryer coredb.QueryRower, workspaceID string, machineID string) (string, error) {
	createdByUserID, err := access.MachineCreatedByUser(ctx, queryer, workspaceID, machineID)
	if err := mapAccessOwnershipError(err); err != nil {
		return "", err
	}
	return createdByUserID, nil
}

func agentManagedByUser(ctx context.Context, queryer coredb.QueryRower, workspaceID string, agentID string) (string, error) {
	createdByUserID, err := access.AgentManagedByUser(ctx, queryer, workspaceID, agentID)
	if err := mapAccessOwnershipError(err); err != nil {
		return "", err
	}
	return createdByUserID, nil
}

func (store *Store) AgentManagedByUser(ctx context.Context, workspaceID string, agentID string) (string, error) {
	return agentManagedByUser(ctx, store.db, workspaceID, agentID)
}

// RequireMachineManagementForUser verifies the user can manage the machine.
// Public wrapper over requireMachineManagement, for the webagent AgentStore
// interface (directory API machine-permission check).
func (store *Store) RequireMachineManagementForUser(ctx context.Context, userID string, workspaceID string, machineID string) error {
	_, err := store.requireMachineManagement(ctx, userID, workspaceID, machineID)
	return err
}

func (store *Store) requireMachineManagement(ctx context.Context, userID string, workspaceID string, machineID string) (access.HumanMachineAccess, error) {
	workspaceMember, membershipErr := store.loadRuntimeWorkspaceMember(ctx, store.db, userID, workspaceID)
	if membershipErr != nil {
		return access.HumanMachineAccess{}, membershipErr
	}
	machine, lookupErr := access.LookupMachine(ctx, store.db, workspaceID, machineID)
	if errors.Is(lookupErr, access.ErrNotFound) {
		return access.HumanMachineAccess{}, ErrNotFound
	}
	if lookupErr != nil {
		return access.HumanMachineAccess{}, fmt.Errorf("load runtime machine: %w", lookupErr)
	}
	createdByUserID, ownerErr := machineCreatedByUser(ctx, store.db, workspaceID, machineID)
	if ownerErr != nil {
		return access.HumanMachineAccess{}, ownerErr
	}
	if strings.TrimSpace(createdByUserID) != strings.TrimSpace(userID) {
		return access.HumanMachineAccess{}, ErrForbidden
	}
	return access.HumanMachineAccess{
		WorkspaceMember: workspaceMember,
		Machine:         machine,
	}, nil
}

func (store *Store) requireAgentOperation(ctx context.Context, userID string, workspaceID string, agentID string) (access.HumanAgentAccess, error) {
	return store.requireAgentOperationWithQueryer(ctx, store.db, userID, workspaceID, agentID)
}

func (store *Store) requireAgentOperationWithQueryer(
	ctx context.Context,
	queryer coredb.QueryRower,
	userID string,
	workspaceID string,
	agentID string,
) (access.HumanAgentAccess, error) {
	workspaceMember, membershipErr := store.loadRuntimeWorkspaceMember(ctx, queryer, userID, workspaceID)
	if membershipErr != nil {
		return access.HumanAgentAccess{}, membershipErr
	}
	agent, lookupErr := access.LookupAgent(ctx, queryer, workspaceID, agentID)
	if errors.Is(lookupErr, access.ErrNotFound) {
		return access.HumanAgentAccess{}, ErrNotFound
	}
	if lookupErr != nil {
		return access.HumanAgentAccess{}, fmt.Errorf("load runtime agent: %w", lookupErr)
	}
	managedByUserID, ownerErr := agentManagedByUser(ctx, queryer, workspaceID, agentID)
	if ownerErr != nil {
		return access.HumanAgentAccess{}, ownerErr
	}
	if strings.TrimSpace(managedByUserID) != strings.TrimSpace(userID) {
		return access.HumanAgentAccess{}, ErrForbidden
	}
	return access.HumanAgentAccess{
		WorkspaceMember: workspaceMember,
		Agent:           agent,
	}, nil
}

func (store *Store) requireAgentWorkspaceRead(ctx context.Context, userID string, workspaceID string, agentID string) (access.HumanAgentAccess, error) {
	return store.requireAgentOperation(ctx, userID, workspaceID, agentID)
}

func (store *Store) requireAgentReviewRead(ctx context.Context, userID string, workspaceID string, agentID string) (access.HumanAgentAccess, error) {
	agentAccess, err := store.requireAgentOperation(ctx, userID, workspaceID, agentID)
	if err == nil {
		return agentAccess, nil
	}
	return access.HumanAgentAccess{}, err
}

func (store *Store) requireAgentDisable(ctx context.Context, userID string, workspaceID string, agentID string) (access.HumanAgentAccess, error) {
	agentAccess, err := store.requireAgentOperation(ctx, userID, workspaceID, agentID)
	if err == nil {
		return agentAccess, nil
	}
	if !errors.Is(err, ErrForbidden) {
		return access.HumanAgentAccess{}, err
	}

	workspaceMember, membershipErr := store.loadRuntimeWorkspaceMember(ctx, store.db, userID, workspaceID)
	if membershipErr != nil {
		return access.HumanAgentAccess{}, membershipErr
	}
	if !access.HasWorkspaceAdminRole(domain.WorkspaceRole(workspaceMember.WorkspaceRole)) {
		return access.HumanAgentAccess{}, ErrForbidden
	}
	agent, lookupErr := access.LookupAgent(ctx, store.db, workspaceID, agentID)
	if errors.Is(lookupErr, access.ErrNotFound) {
		return access.HumanAgentAccess{}, ErrNotFound
	}
	if lookupErr != nil {
		return access.HumanAgentAccess{}, fmt.Errorf("load runtime agent: %w", lookupErr)
	}
	return access.HumanAgentAccess{
		WorkspaceMember: workspaceMember,
		Agent:           agent,
	}, nil
}

func (store *Store) loadAuthorizedHumanDMAccess(ctx context.Context, queryer interface {
	QueryRow(context.Context, string, ...any) coredb.Row
}, userID string, workspaceID string, dmThreadID string, action access.HumanAction) (access.HumanDMAccess, error) {
	dmAccess, err := access.LoadAuthorizedHumanDMAccess(ctx, queryer, userID, workspaceID, dmThreadID, action)
	if errors.Is(err, access.ErrForbidden) {
		return access.HumanDMAccess{}, ErrForbidden
	}
	if errors.Is(err, access.ErrNotFound) {
		return access.HumanDMAccess{}, ErrNotFound
	}
	if err != nil {
		return access.HumanDMAccess{}, fmt.Errorf("load authorized human dm access: %w", err)
	}
	if err := store.requireOwnerPrivateAgentPeerAccess(ctx, queryer, userID, workspaceID, dmAccess.PeerMemberID); err != nil {
		return access.HumanDMAccess{}, err
	}
	return dmAccess, nil
}

func (store *Store) loadAuthorizedHumanThreadAccess(ctx context.Context, queryer interface {
	QueryRow(context.Context, string, ...any) coredb.Row
}, userID string, workspaceID string, threadChannelID string, action access.HumanAction) (access.HumanThreadAccess, error) {
	threadAccess, err := access.LoadAuthorizedHumanThreadAccess(ctx, queryer, userID, workspaceID, threadChannelID, action)
	if errors.Is(err, access.ErrForbidden) {
		return access.HumanThreadAccess{}, ErrForbidden
	}
	if errors.Is(err, access.ErrNotFound) {
		return access.HumanThreadAccess{}, ErrNotFound
	}
	if err != nil {
		return access.HumanThreadAccess{}, fmt.Errorf("load authorized human thread access: %w", err)
	}
	if threadAccess.ParentThreadType == domain.ThreadTypeDM {
		if err := store.requireOwnerPrivateAgentPeerAccess(ctx, queryer, userID, workspaceID, threadAccess.PeerMemberID); err != nil {
			return access.HumanThreadAccess{}, err
		}
	}
	threadAccess.Channel.ThreadID = threadAccess.Channel.ChannelID
	return threadAccess, nil
}

func (store *Store) requireOwnerPrivateAgentPeerAccess(ctx context.Context, queryer interface {
	QueryRow(context.Context, string, ...any) coredb.Row
}, userID string, workspaceID string, peerMemberID string) error {
	trimmedPeerMemberID := strings.TrimSpace(peerMemberID)
	if trimmedPeerMemberID == "" {
		return nil
	}

	row := queryer.QueryRow(ctx, `
SELECT machines.created_by_user_id
FROM agents
JOIN machines
  ON machines.workspace_id = agents.workspace_id
 AND machines.id = agents.machine_id
WHERE agents.workspace_id = $1
  AND agents.member_id = $2
  AND agents.status <> 'archived'
  AND machines.status = 'active'
`, workspaceID, trimmedPeerMemberID)

	var createdByUserID string
	if err := row.Scan(&createdByUserID); err != nil {
		if coredb.IsNotFound(err) {
			return nil
		}
		return fmt.Errorf("load owner-private agent peer: %w", err)
	}
	if strings.TrimSpace(createdByUserID) != strings.TrimSpace(userID) {
		return ErrForbidden
	}
	return nil
}

func (store *Store) resolveAuthorizedHumanMessageTarget(ctx context.Context, userID string, workspaceID string, channelID string, channelAction access.HumanAction, dmAction access.HumanAction, threadAction access.HumanAction) (access.HumanWorkspaceMember, message.ThreadAccess, error) {
	return store.resolveAuthorizedHumanMessageTargetWithQueryer(ctx, store.db, userID, workspaceID, channelID, channelAction, dmAction, threadAction)
}

func (store *Store) resolveAuthorizedHumanMessageTargetWithQueryer(ctx context.Context, queryer humanMessageTargetQueryer, userID string, workspaceID string, channelID string, channelAction access.HumanAction, dmAction access.HumanAction, threadAction access.HumanAction) (access.HumanWorkspaceMember, message.ThreadAccess, error) {
	channel, err := store.channelByID(ctx, queryer, workspaceID, channelID)
	if err == nil {
		switch channel.Type {
		case "channel":
			channelAccess, err := store.loadAuthorizedHumanChannelAccess(ctx, queryer, userID, workspaceID, channelID, channelAction)
			if err != nil {
				return access.HumanWorkspaceMember{}, message.ThreadAccess{}, err
			}
			return channelAccess.WorkspaceMember, message.ThreadAccess{
				ThreadID:    channelAccess.Channel.ThreadID,
				ThreadType:  domain.ThreadTypeChannel,
				ChannelID:   channelAccess.Channel.ChannelID,
				ChannelName: channelAccess.Channel.Slug,
			}, nil
		case "thread":
			threadAccess, err := store.loadAuthorizedHumanThreadAccess(ctx, queryer, userID, workspaceID, channelID, threadAction)
			if err != nil {
				return access.HumanWorkspaceMember{}, message.ThreadAccess{}, err
			}
			if threadAccess.ParentThreadType == domain.ThreadTypeDM && strings.TrimSpace(threadAccess.PeerMemberID) != "" {
				publicNames, err := loadPublicMemberNamesByMemberID(ctx, queryer, workspaceID, []string{threadAccess.PeerMemberID})
				if err != nil {
					return access.HumanWorkspaceMember{}, message.ThreadAccess{}, err
				}
				threadAccess.ParentChannelName = publicMemberNameForMemberID(threadAccess.PeerMemberID, publicNames)
			}
			return threadAccess.WorkspaceMember, message.ThreadAccess{
				ThreadID:     threadAccess.Channel.ThreadID,
				ThreadType:   threadAccess.ParentThreadType,
				ChannelID:    threadAccess.Channel.ChannelID,
				PeerMemberID: threadAccess.PeerMemberID,
				ReplyThread: &message.ReplyThreadTarget{
					ParentMessageID:   threadAccess.ParentMessageID,
					ShortID:           threadAccess.ParentMessageShort,
					ParentChannelID:   threadAccess.ParentChannelID,
					ParentChannelName: threadAccess.ParentChannelName,
				},
			}, nil
		default:
			return access.HumanWorkspaceMember{}, message.ThreadAccess{}, fmt.Errorf("invalid channel type %q", channel.Type)
		}
	}
	if !errors.Is(err, ErrNotFound) {
		return access.HumanWorkspaceMember{}, message.ThreadAccess{}, err
	}

	serviceAccountMember, serviceAccountTarget, found, err := loadAuthorizedHumanServiceAccountMessageTarget(
		ctx,
		queryer,
		userID,
		workspaceID,
		channelID,
		channelAction,
	)
	if err != nil {
		return access.HumanWorkspaceMember{}, message.ThreadAccess{}, err
	}
	if found {
		return serviceAccountMember, serviceAccountTarget, nil
	}

	dmAccess, err := store.loadAuthorizedHumanDMAccess(ctx, queryer, userID, workspaceID, channelID, dmAction)
	if err != nil {
		return access.HumanWorkspaceMember{}, message.ThreadAccess{}, err
	}
	peerPublicName := dmAccess.PeerMemberID
	if strings.TrimSpace(dmAccess.PeerMemberID) != "" {
		publicNames, err := loadPublicMemberNamesByMemberID(ctx, queryer, workspaceID, []string{dmAccess.PeerMemberID})
		if err != nil {
			return access.HumanWorkspaceMember{}, message.ThreadAccess{}, err
		}
		peerPublicName = publicMemberNameForMemberID(dmAccess.PeerMemberID, publicNames)
	}
	return dmAccess.WorkspaceMember, message.ThreadAccess{
		ThreadID:     dmAccess.ThreadID,
		ThreadType:   domain.ThreadTypeDM,
		DMThreadID:   dmAccess.DMThreadID,
		ChannelName:  peerPublicName,
		PeerMemberID: dmAccess.PeerMemberID,
	}, nil
}

func loadAuthorizedHumanServiceAccountMessageTarget(
	ctx context.Context,
	queryer humanMessageTargetQueryer,
	userID string,
	workspaceID string,
	scopeID string,
	action access.HumanAction,
) (access.HumanWorkspaceMember, message.ThreadAccess, bool, error) {
	var accountName string
	err := queryer.QueryRow(ctx, `
SELECT service_accounts.name
FROM scopes
JOIN service_accounts
  ON service_accounts.workspace_id = scopes.workspace_id
 AND service_accounts.message_scope_id = scopes.id
WHERE scopes.workspace_id = $1
  AND scopes.id = $2
  AND scopes.type = 'service_account'
  AND scopes.status = 'active'
`, strings.TrimSpace(workspaceID), strings.TrimSpace(scopeID)).Scan(&accountName)
	if err != nil {
		if coredb.IsNotFound(err) {
			return access.HumanWorkspaceMember{}, message.ThreadAccess{}, false, nil
		}
		return access.HumanWorkspaceMember{}, message.ThreadAccess{}, false, fmt.Errorf("load service account message target: %w", err)
	}

	workspaceMember, err := access.LoadHumanWorkspaceMember(ctx, queryer, userID, workspaceID)
	if err != nil {
		if errors.Is(err, access.ErrForbidden) {
			return access.HumanWorkspaceMember{}, message.ThreadAccess{}, true, ErrForbidden
		}
		if errors.Is(err, access.ErrNotFound) {
			return access.HumanWorkspaceMember{}, message.ThreadAccess{}, true, ErrNotFound
		}
		return access.HumanWorkspaceMember{}, message.ThreadAccess{}, true, fmt.Errorf("load service account workspace member: %w", err)
	}
	var memberCanRead bool
	if err := queryer.QueryRow(ctx, `
SELECT EXISTS (
    SELECT 1
    FROM scope_memberships
    WHERE scope_id = $1
      AND member_id = $2
      AND visibility = 'public'
      AND removed_at IS NULL
      AND archived_at IS NULL
      AND effective_to_seq IS NULL
)
`, strings.TrimSpace(scopeID), workspaceMember.Member.ID).Scan(&memberCanRead); err != nil {
		return access.HumanWorkspaceMember{}, message.ThreadAccess{}, true, fmt.Errorf("check service account audience membership: %w", err)
	}
	if !memberCanRead {
		return access.HumanWorkspaceMember{}, message.ThreadAccess{}, true, ErrForbidden
	}

	// The ordinary Human message writer must never create Human-authored rows
	// in a service-account scope. Publisher writes go through the dedicated
	// service-account command path, which persists the service identity as the
	// durable author and the Human only as the caused-by actor.
	if action.Action != access.ActionRead {
		return access.HumanWorkspaceMember{}, message.ThreadAccess{}, true, ErrForbidden
	}

	return workspaceMember, message.ThreadAccess{
		ThreadID:    strings.TrimSpace(scopeID),
		ThreadType:  domain.ThreadTypeChannel,
		ChannelID:   strings.TrimSpace(scopeID),
		ChannelName: strings.TrimSpace(accountName),
	}, true, nil
}
