package automation

import (
	"context"
	"fmt"
	"strings"

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

const (
	ConnectorRemoteIdentityAuthorityMissing                     = "missing"
	ConnectorRemoteIdentityAuthorityFinalized                   = "finalized"
	ConnectorRemoteIdentityAuthoritySuspendedDrift              = "suspended_drift"
	ConnectorRemoteIdentityAuthorityOwnerDeletedPendingRecovery = ConnectorPushLinkStatusOwnerDeletedPendingRecovery

	ConnectorRemoteIdentityOwnerMissing = "missing"
	ConnectorRemoteIdentityOwnerActive  = "active"
	ConnectorRemoteIdentityOwnerDeleted = "deleted"

	ConnectorRemoteIdentityMirrorNotConfigured = "not_configured"
)

// ConnectorRemoteIdentityResolution is the generic inbound read model. A
// caller may fall back to compatibility lookup only when AuthorityState is
// missing. Any returned owner, including a deleted owner, remains authoritative
// and must never be replaced through an attribute such as Email.
type ConnectorRemoteIdentityResolution struct {
	AuthorityState string
	SourceObject   string
	SourceRecordID string
	OwnerLifecycle string
	MirrorStatus   string
}

func (e *FlowExecutor) executeResolveConnectorRemoteIdentity(
	ctx context.Context,
	interview *models.FlowInterview,
	action *models.FlowActionCall,
	user *models.UserSession,
) (string, error) {
	next := ""
	if action != nil && action.Connector != nil {
		next = action.Connector.TargetReference
	}
	flowIdentity, ok := flowRuntimeContextFromCtx(ctx).CurrentFlow()
	if !ok || !flowIdentityIsPackOwnedStandard(flowIdentity) {
		return "", fmt.Errorf("resolve connector remote identity: Flow must be an uncustomized pack-owned standard FlowVersion")
	}
	connectorInboundAuthorized := constants.ConnectorInboundExecutionAuthorizesFlow(ctx, flowIdentity.APIName, flowIdentity.OwnerPack) ||
		constants.ConnectorInboundCompanionExecutionAuthorizesVersion(ctx, flowIdentity.VersionID)
	standardActionAuthorized := constants.HasStandardActionExecutionContext(ctx) && constants.HasStandardActionProtectedFlowExecution(ctx)
	afterCommitAuthorized := AfterCommitScheduledFlowExecutionAuthorizesVersion(ctx, flowIdentity.VersionID)
	if !connectorInboundAuthorized && !standardActionAuthorized && !afterCommitAuthorized {
		return "", fmt.Errorf("resolve connector remote identity: requires the authorized connector-inbound root or companion Flow, protected standard action context, or exact AsyncAfterCommit FlowVersion")
	}
	store, ok := e.connectorPushDurable.(ConnectorRemoteIdentityAuthorityStore)
	if !ok || store == nil {
		return "", fmt.Errorf("resolve connector remote identity: authority store is not wired")
	}
	if e.query == nil {
		return "", fmt.Errorf("resolve connector remote identity: record query is not wired")
	}
	params := make(map[string]string, len(action.InputParameters))
	for _, parameter := range action.InputParameters {
		resolved, _, err := e.ResolveFlowResource(ctx, interview, user, parameter.Value)
		if err != nil {
			return "", err
		}
		if resolved != nil {
			params[parameter.Name] = strings.TrimSpace(fmt.Sprintf("%v", resolved))
		}
	}
	locator := ConnectorRemoteLocatorQuery{
		ConnectorOwnerKey: params["connectorOwnerKey"],
		DestinationSlotID: params["destinationSlotId"],
		RemoteID:          params["remoteId"],
	}
	trustedOwner := strings.TrimSpace(flowIdentity.OwnerPack)
	if strings.TrimSpace(locator.ConnectorOwnerKey) != trustedOwner {
		return "", fmt.Errorf("resolve connector remote identity: connectorOwnerKey %q does not match executing Flow owner pack %q", locator.ConnectorOwnerKey, trustedOwner)
	}
	locator.ConnectorOwnerKey = trustedOwner
	resolution, err := resolveConnectorRemoteIdentityRecord(ctx, store, e.query, user, locator)
	if err != nil {
		return "", fmt.Errorf("resolve connector remote identity: %w", err)
	}
	assignActionCallOutputs(interview, action, map[string]interface{}{
		"authorityState": resolution.AuthorityState,
		"sourceObject":   resolution.SourceObject,
		"sourceRecordId": resolution.SourceRecordID,
		"ownerLifecycle": resolution.OwnerLifecycle,
		"mirrorStatus":   resolution.MirrorStatus,
	})
	return next, nil
}

func resolveConnectorRemoteIdentityRecord(
	ctx context.Context,
	store ConnectorRemoteIdentityAuthorityStore,
	queryService ports.QueryService,
	user *models.UserSession,
	locator ConnectorRemoteLocatorQuery,
) (ConnectorRemoteIdentityResolution, error) {
	if store == nil {
		return ConnectorRemoteIdentityResolution{}, fmt.Errorf("connector remote identity resolver store is not wired")
	}
	authority, err := store.ResolveFinalizedRemoteLocator(ctx, locator)
	if err != nil {
		return ConnectorRemoteIdentityResolution{}, err
	}
	if authority == nil {
		return ConnectorRemoteIdentityResolution{
			AuthorityState: ConnectorRemoteIdentityAuthorityMissing,
			OwnerLifecycle: ConnectorRemoteIdentityOwnerMissing,
			MirrorStatus:   ConnectorRemoteIdentityMirrorNotConfigured,
		}, nil
	}
	resolution := ConnectorRemoteIdentityResolution{
		AuthorityState: authority.LinkStatus,
		SourceObject:   authority.Identity.SourceObject,
		SourceRecordID: authority.Identity.SourceRecordID,
		MirrorStatus:   ConnectorRemoteIdentityMirrorNotConfigured,
	}
	if resolution.AuthorityState != ConnectorRemoteIdentityAuthorityFinalized &&
		resolution.AuthorityState != ConnectorRemoteIdentityAuthoritySuspendedDrift &&
		resolution.AuthorityState != ConnectorRemoteIdentityAuthorityOwnerDeletedPendingRecovery {
		return ConnectorRemoteIdentityResolution{}, fmt.Errorf("%w: unsupported remote identity authority state %q", ErrConnectorPushDurableConflict, resolution.AuthorityState)
	}
	if resolution.AuthorityState == ConnectorRemoteIdentityAuthorityOwnerDeletedPendingRecovery {
		// Hard-delete hooks can remove the owner row before this resolver runs.
		// The pending durable authority is already the fail-closed result and
		// must not depend on an owner or compatibility-mirror readback.
		resolution.OwnerLifecycle = ConnectorRemoteIdentityOwnerDeleted
		return resolution, nil
	}
	if queryService == nil {
		return ConnectorRemoteIdentityResolution{}, fmt.Errorf("connector remote identity record query is not wired")
	}

	selectFields := []string{constants.FieldID, constants.FieldIsDeleted}
	remoteIDField := strings.TrimSpace(authority.RemoteIDField)
	if remoteIDField != "" {
		selectFields = append(selectFields, remoteIDField)
	}
	result, err := queryService.QueryWithSpec(ctx, queryspec.QuerySpec{
		ObjectAPIName: authority.Identity.SourceObject,
		SelectFields:  selectFields,
		Filter:        queryspec.FieldPredicate(constants.FieldID, "=", authority.Identity.SourceRecordID),
		Limit:         2,
		SkipTotalSize: true,
		ScanAll:       true,
	}, user)
	if err != nil {
		return ConnectorRemoteIdentityResolution{}, fmt.Errorf("read connector remote identity owner %s/%s including tombstones: %w", authority.Identity.SourceObject, authority.Identity.SourceRecordID, err)
	}
	if result == nil || len(result.Records) == 0 {
		return ConnectorRemoteIdentityResolution{}, fmt.Errorf("%w: remote identity authority points to missing owner %s/%s", ErrConnectorPushDurableConflict, authority.Identity.SourceObject, authority.Identity.SourceRecordID)
	}
	if len(result.Records) != 1 {
		return ConnectorRemoteIdentityResolution{}, fmt.Errorf("%w: remote identity authority owner read returned %d rows", ErrConnectorPushDurableConflict, len(result.Records))
	}
	record := result.Records[0]
	if actionRecordString(record, constants.FieldID) != authority.Identity.SourceRecordID {
		return ConnectorRemoteIdentityResolution{}, fmt.Errorf("%w: remote identity authority owner read returned a different record", ErrConnectorPushDurableConflict)
	}
	deletedValue, hasDeleted := record[constants.FieldIsDeleted]
	if !hasDeleted {
		return ConnectorRemoteIdentityResolution{}, fmt.Errorf("%w: remote identity authority owner read omitted IsDeleted", ErrConnectorPushDurableConflict)
	}
	if flowRecordBool(deletedValue) {
		resolution.OwnerLifecycle = ConnectorRemoteIdentityOwnerDeleted
	} else {
		resolution.OwnerLifecycle = ConnectorRemoteIdentityOwnerActive
	}
	if remoteIDField == "" {
		return resolution, nil
	}
	resolution.MirrorStatus = ConnectorPushCompatibilityMirrorDrifted
	if actionRecordString(record, remoteIDField) == strings.TrimSpace(authority.RemoteID) {
		resolution.MirrorStatus = ConnectorPushCompatibilityMirrorSynced
	}
	if err := store.RecordCompatibilityMirrorReadback(ctx, authority.LinkKey, resolution.MirrorStatus); err != nil {
		return ConnectorRemoteIdentityResolution{}, fmt.Errorf("record connector remote identity mirror readback: %w", err)
	}
	return resolution, nil
}
