package automation

import (
	"context"
	"fmt"
	"strings"

	"github.com/nexuscrm/kernel/pkg/flowactions"
	"github.com/nexuscrm/shared/pkg/constants"
	"github.com/nexuscrm/shared/pkg/models"
)

// ConnectorInboundRemoteIdentityRecoveryRequestedError leaves the exact
// selector with the Runtime command owner. The enclosing business transaction
// must roll back before the recovery owner may act.
type ConnectorInboundRemoteIdentityRecoveryRequestedError struct {
	Selector ConnectorInboundRemoteIdentityRecoverySelector
}

func (e *ConnectorInboundRemoteIdentityRecoveryRequestedError) Error() string {
	return "connector inbound remote identity recovery is required"
}

type ConnectorInboundRemoteIdentityRecoveryCandidateProbe interface {
	HasConnectorInboundRemoteIdentityRecoveryCandidate(
		context.Context,
		ConnectorInboundRemoteIdentityRecoverySelector,
	) (bool, error)
}

type connectorInboundRemoteIdentityRecoveryCandidateProbe struct {
	store interface {
		FindInboundRemoteIDEvidenceHintsInAmbientTx(context.Context, ConnectorInboundRemoteIdentityRecoverySelector) ([]ConnectorInboundRemoteIDEvidence, error)
	}
	installed ConnectorInboundRemoteIdentityRecoveryInstalledResolver
}

func NewConnectorInboundRemoteIdentityRecoveryCandidateProbe(
	store interface {
		FindInboundRemoteIDEvidenceHintsInAmbientTx(context.Context, ConnectorInboundRemoteIdentityRecoverySelector) ([]ConnectorInboundRemoteIDEvidence, error)
	},
	installed ConnectorInboundRemoteIdentityRecoveryInstalledResolver,
) ConnectorInboundRemoteIdentityRecoveryCandidateProbe {
	return &connectorInboundRemoteIdentityRecoveryCandidateProbe{store: store, installed: installed}
}

func (p *connectorInboundRemoteIdentityRecoveryCandidateProbe) HasConnectorInboundRemoteIdentityRecoveryCandidate(
	ctx context.Context,
	selector ConnectorInboundRemoteIdentityRecoverySelector,
) (bool, error) {
	if p == nil || p.store == nil || p.installed == nil {
		return false, fmt.Errorf("connector recovery candidate probe is not wired")
	}
	selector, err := normalizeConnectorInboundRemoteIdentityRecoverySelector(selector)
	if err != nil {
		return false, err
	}
	hints, err := p.store.FindInboundRemoteIDEvidenceHintsInAmbientTx(ctx, selector)
	if err != nil {
		return false, err
	}
	installed, err := p.installed.ResolveConnectorInboundRemoteIdentityRecoveryInstalledBindings(ctx, selector)
	if err != nil {
		return false, err
	}
	bindingByKey, _, err := normalizeConnectorInboundRemoteIdentityRecoveryInstalledBindings(selector, installed)
	if err != nil {
		return false, err
	}
	hint, err := selectConnectorInboundRemoteIdentityRecoveryHint(selector, hints, bindingByKey)
	if err != nil {
		return false, err
	}
	if hint == nil {
		return false, nil
	}
	if err := validateConnectorInboundRemoteIdentityRecoveryEvidence(selector, *hint); err != nil {
		return false, err
	}
	return true, nil
}

func ConnectorInboundRemoteIdentityRecoveryRequestFlowActionCallRegistration() FlowActionCallRegistration {
	return FlowActionCallRegistration{
		ActionType:    flowactions.ActionTypeRequestConnectorRemoteIdentityRecovery,
		HandlerRef:    "platform:connector/request-remote-identity-recovery",
		AtomicPolicy:  FlowActionCallAtomicPolicySafe,
		PreviewPolicy: FlowActionCallPreviewPolicyReject,
		Handler: func(ctx context.Context, executor *FlowExecutor, interview *models.FlowInterview, _ *models.FlowMetadata, action *models.FlowActionCall, user *models.UserSession) (string, error) {
			var probe ConnectorInboundRemoteIdentityRecoveryCandidateProbe
			if executor != nil {
				probe = executor.connectorInboundRecoveryProbe
			}
			return executor.executeRequestConnectorRemoteIdentityRecovery(ctx, interview, action, user, probe)
		},
	}
}

func (e *FlowExecutor) executeRequestConnectorRemoteIdentityRecovery(
	ctx context.Context,
	interview *models.FlowInterview,
	action *models.FlowActionCall,
	user *models.UserSession,
	probe ConnectorInboundRemoteIdentityRecoveryCandidateProbe,
) (string, error) {
	next := ""
	if action != nil && action.Connector != nil {
		next = action.Connector.TargetReference
	}
	if e == nil || e.persistence == nil || !isWithinAtomicFlowBoundary(ctx) || !e.persistence.IsTransactionActive(ctx) {
		return "", fmt.Errorf("request connector remote identity recovery: a live atomic Flow transaction is required")
	}
	if isPreviewMode(ctx) {
		return "", fmt.Errorf("request connector remote identity recovery: preview execution is not allowed")
	}
	flowIdentity, ok := flowRuntimeContextFromCtx(ctx).CurrentFlow()
	if !ok || !flowIdentityIsPackOwnedStandard(flowIdentity) || strings.TrimSpace(flowIdentity.OwnerPack) == "" {
		return "", fmt.Errorf("request connector remote identity recovery: Flow must be an uncustomized pack-owned standard FlowVersion")
	}
	connectorNamespace := constants.ConnectorSyncOriginFromContext(ctx)
	if connectorNamespace == "" || !constants.ConnectorInboundCompanionExecutionAuthorizesVersion(ctx, flowIdentity.VersionID) {
		return "", fmt.Errorf("request connector remote identity recovery: exact authenticated companion Flow authority is required")
	}
	params, err := parseConnectorInboundRemoteIdentityRecoveryAction(
		action,
		flowactions.ActionTypeRequestConnectorRemoteIdentityRecovery,
		func(value interface{}) (interface{}, error) {
			resolved, _, err := e.ResolveFlowResource(ctx, interview, user, value)
			return resolved, err
		},
	)
	if err != nil {
		return "", err
	}
	if probe == nil {
		return "", fmt.Errorf("request connector remote identity recovery: candidate probe is not wired")
	}
	selector := ConnectorInboundRemoteIdentityRecoverySelector{
		// Durable connector identity is owned by the exact pack-authored Flow
		// contract, while ConnectorNamespace records the authenticated inbound
		// route. Companion execution intentionally permits those facts to differ.
		ConnectorOwnerKey:  strings.TrimSpace(flowIdentity.OwnerPack),
		ConnectorNamespace: connectorNamespace,
		DestinationSlotID:  params.destinationSlotID,
		RemoteID:           params.remoteID,
	}
	present, err := probe.HasConnectorInboundRemoteIdentityRecoveryCandidate(ctx, selector)
	if err != nil {
		return "", fmt.Errorf("request connector remote identity recovery: inspect exact candidate: %w", err)
	}
	if present {
		return "", &ConnectorInboundRemoteIdentityRecoveryRequestedError{Selector: selector}
	}
	assignActionCallOutputs(interview, action, map[string]interface{}{
		"recoveryState": ConnectorInboundRemoteIdentityRecoveryStateNotFound,
	})
	return next, nil
}
