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"
)

// ResolveWorkflowRunCallbackResponderID resolves the canonical callback Agent
// for a rendered send_message.callback request BEFORE delivery, using the same
// trusted AgentRef/destination-visibility chain as the ordinary message
// transaction, without committing a message. The returned Agent ID is frozen on
// the effect and gates the workspace+Agent execution capacity. Ambiguous or
// invisible responders fail closed so the dispatcher resolves the effect to a
// definitive failure rather than guessing an identity.
func (store *Store) ResolveWorkflowRunCallbackResponderID(
	ctx context.Context,
	workspaceID string,
	ownerUserID string,
	authorMemberID string,
	targetRef string,
	scope *domain.MessageScopeValue,
	responderAgentRef string,
	clientMessageID string,
) (string, error) {
	workspaceID = strings.TrimSpace(workspaceID)
	ownerUserID = strings.TrimSpace(ownerUserID)
	authorMemberID = strings.TrimSpace(authorMemberID)
	responderAgentRef = strings.TrimSpace(responderAgentRef)
	clientMessageID = strings.TrimSpace(clientMessageID)
	if workspaceID == "" || ownerUserID == "" || authorMemberID == "" || responderAgentRef == "" {
		return "", badRequest("workspaceId, ownerUserId, authorMemberId, and responderAgentRef are required")
	}
	var resolved domain.ResolvedMessageTarget
	var err error
	if scope != nil {
		resolved, err = store.ResolveWorkflowRunMessageScope(ctx, workspaceID, ownerUserID, authorMemberID, *scope, clientMessageID)
	} else if targetRef != "" {
		resolved, err = store.ResolveWorkflowRunMessageTargetScope(ctx, workspaceID, ownerUserID, authorMemberID, targetRef, clientMessageID)
	} else {
		return "", badRequest("workflow Run callback requires a target or scope")
	}
	if err != nil {
		return "", err
	}
	return store.resolveWorkflowCallbackResponderForTarget(ctx, workspaceID, authorMemberID, resolved, responderAgentRef)
}

func (store *Store) resolveWorkflowCallbackResponderForTarget(
	ctx context.Context,
	workspaceID string,
	authorMemberID string,
	resolved domain.ResolvedMessageTarget,
	responderAgentRef string,
) (string, error) {
	switch resolved.Kind {
	case domain.MessageTargetKindChannel:
		recipient, err := resolveVisibleChannelAgentRecipientByRef(ctx, store.db, workspaceID, resolved.ScopeID, responderAgentRef, authorMemberID, authorMemberID)
		if err != nil {
			return "", err
		}
		return strings.TrimSpace(recipient.AgentID), nil
	case domain.MessageTargetKindReplyThread:
		parentType, parentScopeID, err := store.loadCallbackReplyThreadParent(ctx, workspaceID, resolved.ScopeID)
		if err != nil {
			return "", err
		}
		if parentType == "channel" {
			recipient, err := resolveVisibleChannelAgentRecipientByRef(ctx, store.db, workspaceID, parentScopeID, responderAgentRef, authorMemberID, authorMemberID)
			if err != nil {
				return "", err
			}
			return strings.TrimSpace(recipient.AgentID), nil
		}
		// A DM reply-thread resolves its responder from the parent DM peer set.
		return store.resolveWorkflowCallbackDMResponder(ctx, workspaceID, authorMemberID, parentScopeID, responderAgentRef)
	case domain.MessageTargetKindDM:
		return store.resolveWorkflowCallbackDMResponder(ctx, workspaceID, authorMemberID, resolved.ScopeID, responderAgentRef)
	case domain.MessageTargetKindTask:
		recipient, err := resolveTaskScopeWorkflowCallbackResponder(ctx, store.db, workspaceID, resolved.ScopeID, responderAgentRef, authorMemberID)
		if err != nil {
			return "", err
		}
		return strings.TrimSpace(recipient.AgentID), nil
	default:
		return "", badRequest("workflow Run callback destination does not support a responder")
	}
}

func (store *Store) resolveWorkflowCallbackDMResponder(
	ctx context.Context,
	workspaceID string,
	authorMemberID string,
	dmScopeID string,
	responderAgentRef string,
) (string, error) {
	target, err := message.ResolveDMThreadByIDForMember(ctx, store.db, authorMemberID, workspaceID, dmScopeID)
	if err != nil {
		return "", mapMessagingError(err)
	}
	var agentID string
	err = coredb.WithTx(ctx, store.db, func(tx coredb.Tx) error {
		recipient, resolveErr := resolveWorkflowCallbackDMResponderByRef(ctx, tx, workspaceID, target, responderAgentRef, authorMemberID)
		if resolveErr != nil {
			return resolveErr
		}
		agentID = strings.TrimSpace(recipient.AgentID)
		return nil
	})
	if err != nil {
		return "", err
	}
	return agentID, nil
}

func (store *Store) loadCallbackReplyThreadParent(ctx context.Context, workspaceID, scopeID string) (string, string, error) {
	var parentID, parentType string
	err := store.db.QueryRow(ctx, `
SELECT COALESCE(parent.id, ''), COALESCE(parent.type, '')
FROM scopes child
LEFT JOIN scopes parent ON parent.id = child.parent_scope_id
WHERE child.workspace_id=$1 AND child.id=$2`, workspaceID, scopeID).Scan(&parentID, &parentType)
	if err != nil {
		return "", "", fmt.Errorf("load workflow Run callback reply-thread parent: %w", err)
	}
	return strings.TrimSpace(parentType), strings.TrimSpace(parentID), nil
}
