package app

import (
	"context"
	"fmt"
	"strings"

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

func loadServiceEventDeliveryTarget(ctx context.Context, queryer coredb.QueryRower, binding serviceEventDeliveryTargetBinding) (message.ThreadAccess, error) {
	switch strings.TrimSpace(binding.TargetKind) {
	case "channel":
		return loadServiceEventChannelDeliveryTarget(ctx, queryer, binding)
	case "thread":
		return loadServiceEventReplyThreadDeliveryTarget(ctx, queryer, binding)
	case "dm":
		return loadServiceEventDMDeliveryTarget(ctx, queryer, binding)
	case "task":
		return message.ThreadAccess{}, badRequest("service event task delivery uses the task discussion surface")
	default:
		return message.ThreadAccess{}, badRequest("service event target kind is not supported")
	}
}

func loadServiceEventChannelDeliveryTarget(ctx context.Context, queryer coredb.QueryRower, binding serviceEventDeliveryTargetBinding) (message.ThreadAccess, error) {
	channelName, err := serviceEventChannelSlugByScope(ctx, queryer, binding.WorkspaceID, binding.DeliveryScopeID)
	if err != nil {
		return message.ThreadAccess{}, err
	}
	return message.ThreadAccess{
		ThreadID:    strings.TrimSpace(binding.DeliveryScopeID),
		ThreadType:  domain.ThreadTypeChannel,
		ChannelID:   strings.TrimSpace(binding.DeliveryScopeID),
		ChannelName: strings.TrimSpace(channelName),
	}, nil
}

func loadServiceEventReplyThreadDeliveryTarget(ctx context.Context, queryer coredb.QueryRower, binding serviceEventDeliveryTargetBinding) (message.ThreadAccess, error) {
	serviceMemberID, err := loadServiceEventVisibleMemberID(ctx, queryer, binding)
	if err != nil {
		return message.ThreadAccess{}, err
	}
	row := queryer.QueryRow(ctx, `
SELECT
    thread_scope.id,
    thread_scope.root_message_id,
    COALESCE(thread_scope.root_message_short_id, ''),
    parent_scope.type,
    parent_scope.id,
    COALESCE(parent_profile.slug, ''),
    COALESCE(dm_peer.id, ''),
    COALESCE(dm_peer.display_name, '')
FROM scopes thread_scope
JOIN scopes parent_scope
  ON parent_scope.workspace_id = thread_scope.workspace_id
 AND parent_scope.id = thread_scope.parent_scope_id
 AND parent_scope.status = 'active'
LEFT JOIN scope_channel_profiles parent_profile
  ON parent_profile.scope_id = parent_scope.id
LEFT JOIN scope_memberships dm_peer_membership
  ON dm_peer_membership.scope_id = parent_scope.id
 AND dm_peer_membership.member_id <> $3
 AND dm_peer_membership.removed_at IS NULL
 AND parent_scope.type = 'dm'
LEFT JOIN members dm_peer
  ON dm_peer.id = dm_peer_membership.member_id
WHERE thread_scope.workspace_id = $1
  AND thread_scope.id = $2
  AND thread_scope.type = 'thread'
  AND thread_scope.status = 'active'
ORDER BY dm_peer_membership.member_id ASC
LIMIT 1
	`, strings.TrimSpace(binding.WorkspaceID), strings.TrimSpace(binding.DeliveryScopeID), serviceMemberID)
	var threadScopeID string
	var parentMessageID string
	var parentMessageShort string
	var parentThreadType string
	var parentScopeID string
	var parentChannelName string
	var peerMemberID string
	var peerName string
	if err := row.Scan(&threadScopeID, &parentMessageID, &parentMessageShort, &parentThreadType, &parentScopeID, &parentChannelName, &peerMemberID, &peerName); err != nil {
		if coredb.IsNotFound(err) {
			return message.ThreadAccess{}, ErrNotFound
		}
		return message.ThreadAccess{}, fmt.Errorf("load service event reply thread delivery target: %w", err)
	}
	target := message.ThreadAccess{
		ThreadID:     strings.TrimSpace(threadScopeID),
		ThreadType:   domain.ThreadTypeChannel,
		ChannelID:    strings.TrimSpace(threadScopeID),
		PeerMemberID: strings.TrimSpace(peerMemberID),
		ReplyThread: &message.ReplyThreadTarget{
			ParentMessageID:   strings.TrimSpace(parentMessageID),
			ShortID:           strings.TrimSpace(parentMessageShort),
			ParentChannelID:   strings.TrimSpace(parentScopeID),
			ParentChannelName: strings.TrimSpace(parentChannelName),
		},
	}
	if strings.TrimSpace(parentThreadType) == string(domain.ThreadTypeDM) {
		target.ThreadType = domain.ThreadTypeDM
		target.DMThreadID = strings.TrimSpace(parentScopeID)
		target.ChannelName = strings.TrimSpace(peerName)
	}
	return target, nil
}

func loadServiceEventDMDeliveryTarget(ctx context.Context, queryer coredb.QueryRower, binding serviceEventDeliveryTargetBinding) (message.ThreadAccess, error) {
	serviceMemberID, err := loadServiceEventVisibleMemberID(ctx, queryer, binding)
	if err != nil {
		return message.ThreadAccess{}, err
	}
	row := queryer.QueryRow(ctx, `
SELECT COALESCE(peer.id, ''), COALESCE(peer.display_name, '')
FROM scopes dm_scope
LEFT JOIN scope_memberships peer_membership
  ON peer_membership.scope_id = dm_scope.id
 AND peer_membership.member_id <> $3
 AND peer_membership.removed_at IS NULL
LEFT JOIN members peer
  ON peer.id = peer_membership.member_id
WHERE dm_scope.workspace_id = $1
  AND dm_scope.id = $2
  AND dm_scope.type = 'dm'
  AND dm_scope.status = 'active'
ORDER BY peer_membership.member_id ASC
LIMIT 1
	`, strings.TrimSpace(binding.WorkspaceID), strings.TrimSpace(binding.DeliveryScopeID), serviceMemberID)
	var peerMemberID string
	var peerName string
	if err := row.Scan(&peerMemberID, &peerName); err != nil {
		if coredb.IsNotFound(err) {
			return message.ThreadAccess{}, ErrNotFound
		}
		return message.ThreadAccess{}, fmt.Errorf("load service event dm delivery target: %w", err)
	}
	return message.ThreadAccess{
		ThreadID:     strings.TrimSpace(binding.DeliveryScopeID),
		ThreadType:   domain.ThreadTypeDM,
		DMThreadID:   strings.TrimSpace(binding.DeliveryScopeID),
		ChannelName:  strings.TrimSpace(peerName),
		PeerMemberID: strings.TrimSpace(peerMemberID),
	}, nil
}

func loadServiceEventVisibleMemberID(ctx context.Context, queryer coredb.QueryRower, binding serviceEventDeliveryTargetBinding) (string, error) {
	if visibleMemberID := strings.TrimSpace(binding.ServiceVisibleMemberID); visibleMemberID != "" {
		return visibleMemberID, nil
	}
	serviceMemberRowID := strings.TrimSpace(binding.ServiceMemberID)
	if serviceMemberRowID == "" {
		return "", badRequest("service event delivery binding member is required")
	}
	var visibleMemberID string
	err := queryer.QueryRow(ctx, `
SELECT member_id
FROM service_members
WHERE workspace_id = $1 AND id = $2
LIMIT 1
`, strings.TrimSpace(binding.WorkspaceID), serviceMemberRowID).Scan(&visibleMemberID)
	if err == nil {
		visibleMemberID = strings.TrimSpace(visibleMemberID)
		if visibleMemberID == "" {
			return "", forbidden("service event visible member is not configured")
		}
		return visibleMemberID, nil
	}
	if coredb.IsNotFound(err) {
		return "", ErrNotFound
	}
	return "", fmt.Errorf("load service event visible member: %w", err)
}
