package connector

// ConnectorPushDurableMetadataContract is the pack-owned L1 contract later
// runtime slices will use to populate durable link/attempt identity and frozen
// write-target metadata. It is intentionally assembled from the pack owner and
// literal source-backed payload only; runtime Flow input values and record data
// are not authority for these fields.
type ConnectorPushDurableMetadataContract struct {
	ConnectorOwnerKey         string
	ActionSlotID              string
	PreviousActionSlotIDs     []string
	ActionType                string
	SameOriginWritePolicy     string
	FlowDeveloperName         string
	FlowActionName            string
	SourceObject              string
	DestinationSlotID         string
	ProfileOwnerPack          string
	ProfileName               string
	TargetObject              string
	BodyRoot                  string
	CreatePathTemplate        string
	UpdatePathTemplate        string
	ResponseIDPath            string
	RemoteIDField             string
	RemoteIDProvenanceField   string
	DiagnosticErrorFieldPaths []string
	CreateConflictResolution  *ConnectorPushCreateConflictResolutionContract
	Associations              []ConnectorPushAssociationContract
}

// ConnectorPushHistoricalReconciliationKeyContract is an explicit, fail-closed
// sidecar declaration for protected historical identity reconciliation.
// SourceField selects one existing outbound field mapping; the runtime derives
// the remote property from that installed mapping instead of accepting a
// second authored copy.
//
// A parser may read this sidecar from the same source-backed component payload,
// but it is deliberately not embedded in ConnectorPushDurableMetadataContract:
// existing contract values, JSON bytes, fingerprints, and unkeyed literals must
// stay unchanged. This declaration does not itself authorize ordinary runtime
// adoption or any remote write.
type ConnectorPushHistoricalReconciliationKeyContract struct {
	SourceField string
}

// ConnectorPushRecoverySourceEligibilityContract is the pack-owned, app-blind
// sidecar used when authored inbound evidence must be rebound to a live local
// record. Each named field must exist on the contract's SourceObject, must be a
// boolean, and must still be false while the recovery transaction holds the
// source-record lock. Product lifecycle facts such as conversion remain in L1
// metadata rather than becoming object-name branches in the recovery engine.
//
// Like historical reconciliation, this sidecar is deliberately not embedded
// in ConnectorPushDurableMetadataContract: enabling recovery must not change
// existing durable contract values, fingerprints, or unkeyed literals.
type ConnectorPushRecoverySourceEligibilityContract struct {
	RequiredFalseBooleanFields []string
}

// ConnectorPushDurableMetadataContractV2 is the explicitly versioned frozen
// projection used by contracts that authorize match-before-create. Keeping the
// V1 Contract nested preserves the source and JSON compatibility of the
// existing exported ConnectorPushDurableMetadataContract, including downstream
// unkeyed composite literals. Future projection changes require a new versioned
// envelope instead of expanding either public struct in place.
type ConnectorPushDurableMetadataContractV2 struct {
	FormatVersion     uint32
	Contract          ConnectorPushDurableMetadataContract
	MatchBeforeCreate *ConnectorPushMatchBeforeCreateContract `json:",omitempty"`
}

// ConnectorPushMatchBeforeCreateContract is the pack-owned policy that must be
// resolved before an outbound create is authorized. Identity selects one
// installed outbound field mapping; Search is a frozen provider-neutral callout
// template. Runtime intent data supplies only the mapped scalar value and can
// never select the identity field, target property, search protocol, or outcome
// policy.
//
// A unique search result is not remote-identity authority by itself. The
// Connector must finalize the resulting RemoteLink through its authoritative
// lifecycle before treating the match as adopted.
type ConnectorPushMatchBeforeCreateContract struct {
	Identity          ConnectorPushMatchIdentityContract
	Search            ConnectorPushMatchSearchContract
	OnZeroMatches     string
	OnOneMatch        string
	OnMultipleMatches string
	OnSearchUncertain string
}

type ConnectorPushMatchIdentityContract struct {
	SourceField     string
	TargetProperty  string
	RequestBodyPath string
	Normalization   string
}

// ConnectorPushMatchSearchContract freezes the provider request and the paths
// required to classify zero, one, or multiple matches. ResponseIDPath is
// relative to one item in ResponseCollectionPath. ResponseTotalPath is required
// so a truncated or paginated collection cannot be mistaken for a unique match.
type ConnectorPushMatchSearchContract struct {
	Method                 string
	PathTemplate           string
	RequestBodyTemplate    interface{}
	ResponseCollectionPath string
	ResponseIDPath         string
	ResponseTotalPath      string
}

// DispositionsValid reports whether the policy uses the only currently
// authorized result transitions. New behavior requires a new reviewed contract
// value rather than a caller-selected fallback.
func (policy ConnectorPushMatchBeforeCreateContract) DispositionsValid() bool {
	return policy.OnZeroMatches == ConnectorPushMatchOnZeroCreate &&
		policy.OnOneMatch == ConnectorPushMatchOnOneAdoptLinkThenPatch &&
		policy.OnMultipleMatches == ConnectorPushMatchOnMultipleRejectAmbiguous &&
		policy.OnSearchUncertain == ConnectorPushMatchOnUncertainRetryWithoutCreate
}

func ValidConnectorPushMatchNormalization(value string) bool {
	switch value {
	case ConnectorPushMatchNormalizationExact,
		ConnectorPushMatchNormalizationTrim,
		ConnectorPushMatchNormalizationEmail:
		return true
	default:
		return false
	}
}

type ConnectorPushCreateConflictResolutionContract struct {
	StatusCodes     []int
	DiagnosticMatch map[string]string
	Identity        ConnectorPushConflictIdentityContract
	Resolve         ConnectorPushConflictResolverContract
	OnResolved      string
	// NoOldSnapshotPolicy is an explicit save-time fallback for creates that
	// cannot derive the sparse PATCH required by OnResolved. Policies that
	// reuse a create payload require the pack to certify that the authorized,
	// serialized create body is also valid as an update body.
	NoOldSnapshotPolicy string
}

type ConnectorPushConflictIdentityContract struct {
	SourceField     string
	TargetProperty  string
	RequestBodyPath string
	Normalization   string
}

type ConnectorPushConflictResolverContract struct {
	Method            string
	PathTemplate      string
	ResponseIDPath    string
	RequireExactlyOne bool
}

type ConnectorPushAssociationContract struct {
	SourceLookupField  string
	Junction           *ConnectorPushAssociationJunctionContract
	SourceObject       string
	RemoteIDField      string
	TargetActionSlotID string
	// Derived from the matched target connector_push_contract during validation;
	// pack JSON must not author association-specific target lineage. The list is
	// used as runtime lookup fallback slots and is intentionally excluded from the
	// durable body fingerprint.
	TargetPreviousActionSlotIDs []string
	// Derived from the matched target connector_push_contract during validation;
	// association callouts still use the source profile, but the target durable
	// link must be validated against the profile that created the target link.
	// These fields are intentionally excluded from the durable body fingerprint.
	TargetProfileOwnerPack  string
	TargetProfileName       string
	TargetDestinationSlotID string
	TargetObject            string
	PayloadField            string
	PayloadTemplate         interface{}
	PayloadMergeMode        string
	InlinePhases            []string
	AssociationPath         string
	AssociationMethod       string
	CalloutBodyTemplate     interface{}
	CalloutPhases           []string
}

type ConnectorPushAssociationJunctionContract struct {
	Object            string
	SourceLookupField string
	TargetLookupField string
	Filters           []ConnectorPushAssociationJunctionFilter
	Limit             int
}

type ConnectorPushAssociationJunctionFilter struct {
	Field  string
	Equals interface{}
}

type ConnectorPushContractValidationOptions struct {
	ObjectFields                   map[string][]ConnectorPushObjectFieldMetadata
	RequireSourceLookupFieldSchema bool
}

type ConnectorPushObjectFieldMetadata struct {
	APIName     string
	Type        string
	ReferenceTo []string
}

const (
	ConnectorPushDurableMetadataContractV2FormatVersion = uint32(2)

	ConnectorPushSameOriginWritePolicySuppress           = "suppress"
	ConnectorPushSameOriginWritePolicyAllowPatchExisting = "allowPatchExisting"

	ConnectorPushConflictNormalizationEmail                                           = "email"
	ConnectorPushConflictOnResolvedAdoptLinkThenPatch                                 = "adoptLinkThenPatch"
	ConnectorPushConflictNoOldSnapshotAdoptLinkOnly                                   = "adoptLinkOnly"
	ConnectorPushConflictNoOldSnapshotAdoptLinkThenPatchAuthorizedFrozenCreatePayload = "adoptLinkThenPatchAuthorizedFrozenCreatePayload"

	ConnectorPushMatchNormalizationExact = "exact"
	ConnectorPushMatchNormalizationTrim  = "trim"
	ConnectorPushMatchNormalizationEmail = "email"

	ConnectorPushMatchIdentityTemplateToken       = "{identity}"
	ConnectorPushMatchTargetPropertyTemplateToken = "{targetProperty}"

	ConnectorPushMatchOnZeroCreate                  = "create"
	ConnectorPushMatchOnOneAdoptLinkThenPatch       = "adoptLinkThenPatch"
	ConnectorPushMatchOnMultipleRejectAmbiguous     = "rejectAmbiguous"
	ConnectorPushMatchOnUncertainRetryWithoutCreate = "retryWithoutCreate"
)
