package app

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

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

type channelShareCardRecord struct {
	ID                 string
	WorkspaceID        string
	ChannelID          string
	SourceMessageID    string
	AnchorMessageID    string
	TargetScopeID    string
	CreatedByMemberID  string
	RevokedAt          *time.Time
	RevokedByMemberID  string
	MessageType        string
	Payload            []byte
	FallbackName       string
	FallbackDesc       string
	FallbackVisibility string
}

func channelShareCardDisplayName(channel access.Channel) string {
	for _, candidate := range []string{channel.DisplayName, channel.Slug, channel.ChannelID} {
		if trimmed := strings.TrimSpace(candidate); trimmed != "" {
			return trimmed
		}
	}
	return ""
}

func channelShareCardRootMessageID(record channelShareCardRecord) string {
	if sourceMessageID := strings.TrimSpace(record.SourceMessageID); sourceMessageID != "" {
		return sourceMessageID
	}
	return strings.TrimSpace(record.AnchorMessageID)
}

func channelSupportsShareCards(channel access.Channel) bool {
	return strings.TrimSpace(channel.Subsystem) == message.ChannelSubsystemIM
}

func channelShareCardSnapshot(channel access.Channel) map[string]any {
	displayName := channelShareCardDisplayName(channel)
	return map[string]any{
		"title":              displayName,
		"summary":            strings.TrimSpace(channel.Description),
		"channelId":          strings.TrimSpace(channel.ChannelID),
		"channelSlug":        strings.TrimSpace(channel.Slug),
		"channelName":        displayName,
		"channelDisplayName": displayName,
		"channelDescription": strings.TrimSpace(channel.Description),
		"channelVisibility":  strings.TrimSpace(channel.Visibility),
	}
}

func channelShareCardMessageMetadata(channel access.Channel) map[string]any {
	return map[string]any{
		"channelShare": channelShareCardSnapshot(channel),
	}
}

func readStringFromRecord(record map[string]any, key string) string {
	value, ok := record[key].(string)
	if !ok {
		return ""
	}
	return strings.TrimSpace(value)
}

func readRecordFromRecord(record map[string]any, key string) map[string]any {
	raw, ok := record[key]
	if !ok || raw == nil {
		return nil
	}
	nested, ok := raw.(map[string]any)
	if !ok {
		return nil
	}
	return nested
}

func readChannelShareCardFallback(metadata map[string]any) (string, string, string) {
	if metadata == nil {
		return "", "", ""
	}
	for _, record := range []map[string]any{
		readRecordFromRecord(metadata, "channelShare"),
		metadata,
	} {
		if record == nil {
			continue
		}
		name := readStringFromRecord(record, "channelDisplayName")
		if name == "" {
			name = readStringFromRecord(record, "displayName")
		}
		if name == "" {
			name = readStringFromRecord(record, "channelName")
		}
		if name == "" {
			name = strings.TrimPrefix(readStringFromRecord(record, "title"), "#")
		}
		description := readStringFromRecord(record, "channelDescription")
		if description == "" {
			description = readStringFromRecord(record, "description")
		}
		if description == "" {
			description = readStringFromRecord(record, "summary")
		}
		visibility := readStringFromRecord(record, "channelVisibility")
		if visibility == "" {
			visibility = readStringFromRecord(record, "visibility")
		}
		if name != "" || description != "" || visibility != "" {
			return name, description, visibility
		}
	}
	return "", "", ""
}

func (store *Store) CreateChannelShareCardForUser(ctx context.Context, user model.User, workspaceID string, sourceChannelID string, targetChannelID string) (message.AppMessage, message.MessageDelivery, error) {
	var appMessage message.AppMessage
	var delivery message.MessageDelivery
	tx, err := store.db.BeginTx(ctx, coredb.TxOptions{})
	if err != nil {
		return message.AppMessage{}, message.MessageDelivery{}, fmt.Errorf("begin create channel share card tx: %w", err)
	}
	defer rollback(tx, ctx)

	channelAccess, err := store.loadAuthorizedHumanChannelAccess(ctx, tx, user.ID, workspaceID, strings.TrimSpace(sourceChannelID), channelManageMembersAction)
	if err != nil {
		return message.AppMessage{}, message.MessageDelivery{}, err
	}
	sourceChannel := channelAccess.Channel
	if sourceChannel.Type != "channel" {
		return message.AppMessage{}, message.MessageDelivery{}, badRequest("thread channels cannot be shared")
	}
	if sourceChannel.Slug == "all" {
		return message.AppMessage{}, message.MessageDelivery{}, badRequest("the #all channel cannot be shared")
	}
	if !channelSupportsShareCards(sourceChannel) {
		return message.AppMessage{}, message.MessageDelivery{}, badRequest("subsystem channels cannot be shared")
	}
	if sourceChannel.Visibility != "private" && sourceChannel.Visibility != "public" {
		return message.AppMessage{}, message.MessageDelivery{}, badRequest("channel cannot be shared with a join card")
	}
	if sourceChannel.OwnerMemberID == nil {
		return message.AppMessage{}, message.MessageDelivery{}, ErrNotFound
	}

	targetID := strings.TrimSpace(targetChannelID)
	if targetID == "" {
		return message.AppMessage{}, message.MessageDelivery{}, badRequest("targetChannelId is required")
	}

	cardID, err := identifiers.NewEntityID("csc")
	if err != nil {
		return message.AppMessage{}, message.MessageDelivery{}, fmt.Errorf("generate channel share card id: %w", err)
	}
	content := fmt.Sprintf("Shared channel %s", channelShareCardDisplayName(sourceChannel))
	prepared, err := store.prepareAppMessageForUserTx(ctx, tx, user, workspaceID, targetID, content, nil, SendAppMessageOptions{
		MessageType: MessageTypeChannelShareCard,
	})
	if err != nil {
		return message.AppMessage{}, message.MessageDelivery{}, err
	}
	targetScopeID := appChannelIDForTarget(prepared.target)
	if targetScopeID == "" {
		return message.AppMessage{}, message.MessageDelivery{}, badRequest("target channel is required")
	}
	if prepared.target.ThreadType != domain.ThreadTypeChannel || prepared.target.ReplyThread != nil || strings.TrimSpace(prepared.target.ChannelID) != targetScopeID {
		return message.AppMessage{}, message.MessageDelivery{}, badRequest("share cards can only be posted to channels")
	}

	metadata := channelShareCardMessageMetadata(sourceChannel)
	delivery, err = store.appendMessageToThread(ctx, tx, prepared.membership.Member.ID, domain.MemberTypeHuman, prepared.target, prepared.trimmedContent, prepared.normalizedAttachmentIDs, appendMessageOptions{
		MessageType:         MessageTypeChannelShareCard,
		ExplicitRoutingPlan: prepared.explicitRoutingPlan,
		Metadata:            metadata,
	}, messageAppendCallerOwned)
	if err != nil {
		return message.AppMessage{}, message.MessageDelivery{}, err
	}

	createdAt := delivery.Message.Timestamp
	shareCardPayload, _, err := message.EncodeMessageMetadata(channelShareCardSnapshot(sourceChannel))
	if err != nil {
		return message.AppMessage{}, message.MessageDelivery{}, err
	}
	if err := message.NewScopeRepositories(tx).ShareCards.Insert(ctx, message.ScopeShareCardRecord{
		ID:                cardID,
		WorkspaceID:       workspaceID,
		ScopeID:           targetScopeID,
		MessageID:         delivery.MessageID,
		SourceScopeID:     sourceChannel.ChannelID,
		TargetScopeID:     targetScopeID,
		CreatedByMemberID: prepared.membership.Member.ID,
		Payload:           shareCardPayload,
		CreatedAt:         createdAt,
		UpdatedAt:         createdAt,
	}); err != nil {
		return message.AppMessage{}, message.MessageDelivery{}, err
	}

	appMessage = appMessageFromDelivery(
		targetScopeID,
		humanAuthorProfileFromUser(user, prepared.membership.Member.ID, prepared.membership.Member.DisplayName),
		delivery,
	)
	if err := attachResolvedMentionsForSentAppMessage(ctx, tx, workspaceID, &appMessage); err != nil {
		return message.AppMessage{}, message.MessageDelivery{}, err
	}
	attachAppMessageToDelivery(&delivery, &appMessage)
	if err := store.persistScopeFanoutPayloads(ctx, tx, delivery); err != nil {
		return message.AppMessage{}, message.MessageDelivery{}, err
	}

	if err := tx.Commit(ctx); err != nil {
		return message.AppMessage{}, message.MessageDelivery{}, fmt.Errorf("commit create channel share card: %w", err)
	}

	return appMessage, delivery, nil
}

func (store *Store) ShareChannelShareCardForUser(ctx context.Context, user model.User, workspaceID string, messageID string, targetChannelID string) (message.AppMessage, message.MessageDelivery, error) {
	var appMessage message.AppMessage
	var delivery message.MessageDelivery
	tx, err := store.db.BeginTx(ctx, coredb.TxOptions{})
	if err != nil {
		return message.AppMessage{}, message.MessageDelivery{}, fmt.Errorf("begin share channel share card tx: %w", err)
	}
	defer rollback(tx, ctx)

	_, record, err := store.loadChannelShareCardRecordForUser(ctx, tx, user.ID, workspaceID, messageID, "FOR UPDATE")
	if err != nil {
		return message.AppMessage{}, message.MessageDelivery{}, err
	}
	if record.RevokedAt != nil {
		return message.AppMessage{}, message.MessageDelivery{}, gone("channel share card has been revoked")
	}

	channelAccess, err := store.loadAuthorizedHumanChannelAccess(ctx, tx, user.ID, workspaceID, record.ChannelID, channelManageMembersAction)
	if err != nil {
		return message.AppMessage{}, message.MessageDelivery{}, err
	}
	sourceChannel := channelAccess.Channel
	if sourceChannel.Type != "channel" {
		return message.AppMessage{}, message.MessageDelivery{}, badRequest("thread channels cannot be shared")
	}
	if sourceChannel.Slug == "all" {
		return message.AppMessage{}, message.MessageDelivery{}, badRequest("the #all channel cannot be shared")
	}
	if !channelSupportsShareCards(sourceChannel) {
		return message.AppMessage{}, message.MessageDelivery{}, badRequest("subsystem channels cannot be shared")
	}
	if sourceChannel.Visibility != "private" && sourceChannel.Visibility != "public" {
		return message.AppMessage{}, message.MessageDelivery{}, badRequest("channel cannot be shared with a join card")
	}
	if sourceChannel.OwnerMemberID == nil {
		return message.AppMessage{}, message.MessageDelivery{}, ErrNotFound
	}

	targetID := strings.TrimSpace(targetChannelID)
	if targetID == "" {
		return message.AppMessage{}, message.MessageDelivery{}, badRequest("targetChannelId is required")
	}

	cardID, err := identifiers.NewEntityID("csc")
	if err != nil {
		return message.AppMessage{}, message.MessageDelivery{}, fmt.Errorf("generate channel share card id: %w", err)
	}
	content := fmt.Sprintf("Shared channel %s", channelShareCardDisplayName(sourceChannel))
	prepared, err := store.prepareAppMessageForUserTx(ctx, tx, user, workspaceID, targetID, content, nil, SendAppMessageOptions{
		MessageType: MessageTypeChannelShareCard,
	})
	if err != nil {
		return message.AppMessage{}, message.MessageDelivery{}, err
	}
	targetScopeID := appChannelIDForTarget(prepared.target)
	if targetScopeID == "" {
		return message.AppMessage{}, message.MessageDelivery{}, badRequest("target channel is required")
	}
	if prepared.target.ThreadType != domain.ThreadTypeChannel || prepared.target.ReplyThread != nil || strings.TrimSpace(prepared.target.ChannelID) != targetScopeID {
		return message.AppMessage{}, message.MessageDelivery{}, badRequest("share cards can only be posted to channels")
	}

	metadata := channelShareCardMessageMetadata(sourceChannel)
	delivery, err = store.appendMessageToThread(ctx, tx, prepared.membership.Member.ID, domain.MemberTypeHuman, prepared.target, prepared.trimmedContent, prepared.normalizedAttachmentIDs, appendMessageOptions{
		MessageType:         MessageTypeChannelShareCard,
		ExplicitRoutingPlan: prepared.explicitRoutingPlan,
		Metadata:            metadata,
	}, messageAppendCallerOwned)
	if err != nil {
		return message.AppMessage{}, message.MessageDelivery{}, err
	}

	createdAt := delivery.Message.Timestamp
	shareCardPayload, _, err := message.EncodeMessageMetadata(channelShareCardSnapshot(sourceChannel))
	if err != nil {
		return message.AppMessage{}, message.MessageDelivery{}, err
	}
	if err := message.NewScopeRepositories(tx).ShareCards.Insert(ctx, message.ScopeShareCardRecord{
		ID:                cardID,
		WorkspaceID:       workspaceID,
		ScopeID:           targetScopeID,
		MessageID:         delivery.MessageID,
		SourceScopeID:     sourceChannel.ChannelID,
		SourceMessageID:   channelShareCardRootMessageID(record),
		TargetScopeID:     targetScopeID,
		CreatedByMemberID: prepared.membership.Member.ID,
		Payload:           shareCardPayload,
		CreatedAt:         createdAt,
		UpdatedAt:         createdAt,
	}); err != nil {
		return message.AppMessage{}, message.MessageDelivery{}, err
	}

	appMessage = appMessageFromDelivery(
		targetScopeID,
		humanAuthorProfileFromUser(user, prepared.membership.Member.ID, prepared.membership.Member.DisplayName),
		delivery,
	)
	if err := attachResolvedMentionsForSentAppMessage(ctx, tx, workspaceID, &appMessage); err != nil {
		return message.AppMessage{}, message.MessageDelivery{}, err
	}
	attachAppMessageToDelivery(&delivery, &appMessage)
	if err := store.persistScopeFanoutPayloads(ctx, tx, delivery); err != nil {
		return message.AppMessage{}, message.MessageDelivery{}, err
	}

	if err := tx.Commit(ctx); err != nil {
		return message.AppMessage{}, message.MessageDelivery{}, fmt.Errorf("commit share channel share card: %w", err)
	}

	return appMessage, delivery, nil
}

func (store *Store) loadChannelShareCardRecordForUser(ctx context.Context, queryer coredb.QueryExecer, userID string, workspaceID string, messageID string, lockClause string) (access.HumanWorkspaceMember, channelShareCardRecord, error) {
	trimmedMessageID := strings.TrimSpace(messageID)
	if trimmedMessageID == "" {
		return access.HumanWorkspaceMember{}, channelShareCardRecord{}, badRequest("messageId is required")
	}

	query := `
SELECT
    sc.id,
    sc.workspace_id,
    sc.source_scope_id,
    COALESCE(sc.source_message_id, ''),
    sc.message_id,
    sc.scope_id,
    sc.created_by_member_id,
    sc.revoked_at,
    COALESCE(sc.revoked_by_member_id, ''),
    sm.message_type,
    sc.payload
FROM scope_share_cards sc
JOIN scope_messages sm
  ON sm.workspace_id = sc.workspace_id
 AND sm.scope_id = sc.scope_id
 AND sm.id = sc.message_id
WHERE sc.workspace_id = $1
  AND sc.message_id = $2
`
	if strings.TrimSpace(lockClause) != "" {
		query += "\n" + strings.TrimSpace(lockClause)
	}

	var record channelShareCardRecord
	var payload []byte
	if err := queryer.QueryRow(ctx, query, strings.TrimSpace(workspaceID), trimmedMessageID).Scan(
		&record.ID,
		&record.WorkspaceID,
		&record.ChannelID,
		&record.SourceMessageID,
		&record.AnchorMessageID,
		&record.TargetScopeID,
		&record.CreatedByMemberID,
		&record.RevokedAt,
		&record.RevokedByMemberID,
		&record.MessageType,
		&payload,
	); err != nil {
		if coredb.IsNotFound(err) {
			return access.HumanWorkspaceMember{}, channelShareCardRecord{}, ErrNotFound
		}
		return access.HumanWorkspaceMember{}, channelShareCardRecord{}, fmt.Errorf("load channel share card: %w", err)
	}
	if strings.TrimSpace(record.MessageType) != MessageTypeChannelShareCard {
		return access.HumanWorkspaceMember{}, channelShareCardRecord{}, ErrNotFound
	}
	record.Payload = append([]byte(nil), payload...)
	fallback, err := message.DecodeMessageMetadata(payload)
	if err != nil {
		return access.HumanWorkspaceMember{}, channelShareCardRecord{}, err
	}
	record.FallbackName, record.FallbackDesc, record.FallbackVisibility = readChannelShareCardFallback(fallback)

	membership, err := store.loadAuthorizedHumanWorkspaceMember(ctx, queryer, userID, workspaceID, workspaceAccessAction)
	if err != nil {
		return access.HumanWorkspaceMember{}, channelShareCardRecord{}, err
	}
	if err := store.authorizeMessageSidecarReadForMember(ctx, queryer, membership.Member.ID, workspaceID, record.AnchorMessageID, message.SidecarKindShareCard); err != nil {
		return access.HumanWorkspaceMember{}, channelShareCardRecord{}, err
	}
	return membership, record, nil
}

func (store *Store) channelShareCardView(ctx context.Context, queryer humanMessageTargetQueryer, membership access.HumanWorkspaceMember, record channelShareCardRecord) (message.ChannelShareCardView, error) {
	view := message.ChannelShareCardView{
		MessageID:     strings.TrimSpace(record.AnchorMessageID),
		CardID:        strings.TrimSpace(record.ID),
		CardChannelID: strings.TrimSpace(record.TargetScopeID),
		ChannelID:     strings.TrimSpace(record.ChannelID),
		Name:          strings.TrimSpace(record.FallbackName),
		Description:   strings.TrimSpace(record.FallbackDesc),
		Visibility:    strings.TrimSpace(record.FallbackVisibility),
		Revoked:       record.RevokedAt != nil,
	}
	if record.RevokedAt != nil && !record.RevokedAt.IsZero() {
		view.RevokedAt = record.RevokedAt.UTC().Format(time.RFC3339Nano)
	}

	sourceAvailable := true
	channel, err := store.channelByID(ctx, queryer, membership.Member.WorkspaceID, record.ChannelID)
	if err != nil {
		if !errors.Is(err, ErrNotFound) {
			return message.ChannelShareCardView{}, err
		}
		sourceAvailable = false
	} else if channel.Type != "channel" || !channelSupportsShareCards(channel) || (channel.Visibility != "private" && channel.Visibility != "public") || channel.OwnerMemberID == nil {
		sourceAvailable = false
	}

	var joined bool
	var isOwner bool
	if sourceAvailable {
		view.Name = channelShareCardDisplayName(channel)
		view.Description = strings.TrimSpace(channel.Description)
		view.Visibility = strings.TrimSpace(channel.Visibility)
		joined, err = access.IsPublicMemberInChannel(ctx, queryer, membership.Member.ID, channel.ChannelID)
		if err != nil {
			return message.ChannelShareCardView{}, err
		}
		isOwner = channel.OwnerMemberID != nil && strings.TrimSpace(*channel.OwnerMemberID) == strings.TrimSpace(membership.Member.ID)
	}

	view.Joined = joined
	view.CanOpen = joined
	view.CanRevoke = sourceAvailable && isOwner && !view.Revoked
	switch {
	case !sourceAvailable:
		view.Status = "unavailable"
	case joined:
		view.Status = "joined"
	case view.Revoked:
		view.Status = "revoked"
	default:
		view.Status = "joinable"
		view.CanJoin = true
	}
	return view, nil
}

func (store *Store) LoadChannelShareCardForUser(ctx context.Context, userID string, workspaceID string, messageID string) (message.ChannelShareCardView, error) {
	membership, record, err := store.loadChannelShareCardRecordForUser(ctx, store.db, userID, workspaceID, messageID, "")
	if err != nil {
		return message.ChannelShareCardView{}, err
	}
	return store.channelShareCardView(ctx, store.db, membership, record)
}

func channelSummaryFromShareCardChannel(channel access.Channel, joined bool, isOwner bool) message.ChannelSummary {
	return message.ChannelSummary{
		ChannelID:   channel.ChannelID,
		Slug:        channel.Slug,
		DisplayName: channel.DisplayName,
		Description: channel.Description,
		Visibility:  channel.Visibility,
		Joined:      joined,
		CanInvite:   joined,
		CanManage:   joined && isOwner,
		Archived:    false,
	}
}

func (store *Store) JoinChannelShareCardForUser(ctx context.Context, userID string, workspaceID string, messageID string) (message.ChannelSummary, error) {
	tx, err := store.db.BeginTx(ctx, coredb.TxOptions{})
	if err != nil {
		return message.ChannelSummary{}, fmt.Errorf("begin join channel share card tx: %w", err)
	}
	defer rollback(tx, ctx)

	membership, record, err := store.loadChannelShareCardRecordForUser(ctx, tx, userID, workspaceID, messageID, "FOR UPDATE")
	if err != nil {
		return message.ChannelSummary{}, err
	}
	channel, err := store.channelByID(ctx, tx, membership.Member.WorkspaceID, record.ChannelID)
	if err != nil {
		if errors.Is(err, ErrNotFound) {
			return message.ChannelSummary{}, ErrNotFound
		}
		return message.ChannelSummary{}, err
	}
	if channel.Type != "channel" || !channelSupportsShareCards(channel) || (channel.Visibility != "private" && channel.Visibility != "public") || channel.OwnerMemberID == nil {
		return message.ChannelSummary{}, ErrNotFound
	}
	joined, err := access.IsPublicMemberInChannel(ctx, tx, membership.Member.ID, channel.ChannelID)
	if err != nil {
		return message.ChannelSummary{}, err
	}
	isOwner := channel.OwnerMemberID != nil && strings.TrimSpace(*channel.OwnerMemberID) == strings.TrimSpace(membership.Member.ID)
	if joined {
		return channelSummaryFromShareCardChannel(channel, true, isOwner), nil
	}
	if record.RevokedAt != nil {
		return message.ChannelSummary{}, gone("channel share card has been revoked")
	}

	if err := store.ensureChannelScopeParticipantProjection(ctx, tx, channel.ChannelID, membership.Member.ID, "no-wake", time.Now().UTC()); err != nil {
		return message.ChannelSummary{}, err
	}
	if err := tx.Commit(ctx); err != nil {
		return message.ChannelSummary{}, fmt.Errorf("commit join channel share card: %w", err)
	}
	return channelSummaryFromShareCardChannel(channel, true, false), nil
}

func (store *Store) RevokeChannelShareCardForUser(ctx context.Context, userID string, workspaceID string, messageID string) (message.ChannelShareCardView, error) {
	tx, err := store.db.BeginTx(ctx, coredb.TxOptions{})
	if err != nil {
		return message.ChannelShareCardView{}, fmt.Errorf("begin revoke channel share card tx: %w", err)
	}
	defer rollback(tx, ctx)

	_, record, err := store.loadChannelShareCardRecordForUser(ctx, tx, userID, workspaceID, messageID, "FOR UPDATE")
	if err != nil {
		return message.ChannelShareCardView{}, err
	}
	channelAccess, err := store.loadAuthorizedHumanChannelAccess(ctx, tx, userID, workspaceID, record.ChannelID, channelManageMembersAction)
	if err != nil {
		return message.ChannelShareCardView{}, err
	}
	if channelAccess.Channel.Type != "channel" || !channelSupportsShareCards(channelAccess.Channel) || (channelAccess.Channel.Visibility != "private" && channelAccess.Channel.Visibility != "public") || channelAccess.Channel.OwnerMemberID == nil {
		return message.ChannelShareCardView{}, ErrNotFound
	}

	if record.RevokedAt == nil {
		revokedAt := time.Now().UTC()
		rootMessageID := channelShareCardRootMessageID(record)
		if _, err := tx.Exec(ctx, `
UPDATE scope_share_cards
SET revoked_at = $3,
    revoked_by_member_id = $4,
    updated_at = $3
WHERE workspace_id = $1
  AND (message_id = $2 OR source_message_id = $2)
  AND revoked_at IS NULL
`, strings.TrimSpace(workspaceID), rootMessageID, revokedAt, channelAccess.WorkspaceMember.Member.ID); err != nil {
			return message.ChannelShareCardView{}, fmt.Errorf("revoke scope share card: %w", err)
		}
	}

	if err := tx.Commit(ctx); err != nil {
		return message.ChannelShareCardView{}, fmt.Errorf("commit revoke channel share card: %w", err)
	}
	return store.LoadChannelShareCardForUser(ctx, userID, workspaceID, messageID)
}
