package connector_callout

import (
	"encoding/json"
	"net/http"
	"net/url"
)

// CalloutRequest describes a single outbound HTTP request to be executed by
// Engine.Do. All fields except CredentialRef and EndpointClass are optional
// (empty Method defaults to GET inside Engine.Do).
type CalloutRequest struct {
	// Method is the HTTP method (GET, POST, PUT, PATCH, DELETE). Defaults to
	// GET when empty.
	Method string

	// RelativePath is appended to the base endpoint URL resolved from
	// CredentialRef. May be empty for root-level requests.
	// `@` is allowed in path segments (e.g. /users/alice@example.com); the
	// engine rejects only `?` query and `#` fragment characters at preflight,
	// and rejects actual URL userinfo (user:pass@host) via post-composition
	// url.Parse check.
	RelativePath string

	// Headers contains extra HTTP headers to include in the request. The
	// Authorization header is set internally from the resolved credential;
	// do not set it here.
	Headers http.Header

	// QueryParams are appended to the composed URL as a query string. The
	// engine encodes values via url.Values.Encode(), so callers do not need
	// to percent-encode values themselves; reserved characters (&, =, space,
	// unicode, …) round-trip safely.
	QueryParams url.Values

	// Body is the raw request body. Ignored when Method is GET.
	Body []byte

	// CredentialRef is the DeveloperName of the NamedCredential used to
	// resolve both the base endpoint URL and the bearer token.
	CredentialRef string

	// EndpointClass is the logical endpoint group forwarded to the throttler
	// (e.g. "crm.objects.read"). Must be non-empty.
	EndpointClass string
}

// CalloutResponse holds the raw HTTP response returned by Engine.Do.
type CalloutResponse struct {
	// StatusCode is the HTTP status code.
	StatusCode int

	// Header contains the response HTTP headers.
	Header http.Header

	// Body is the raw response body, bounded by Engine.MaxResponseBytes.
	Body []byte
}

// PageSpec configures cursor-based pagination for Engine.Paginate.
type PageSpec struct {
	// NextCursorJSONPath is a dotted JSON path to the cursor value in each
	// response body (e.g. "paging.next.after"). When the path is absent or
	// the value is empty/null, pagination stops.
	NextCursorJSONPath string

	// CursorRequestParam is the query-parameter name used to pass the cursor
	// to the next page request (e.g. "after").
	CursorRequestParam string

	// PageSizeRequestParam is the query-parameter name for the page size hint.
	// Leave empty to omit the page-size parameter.
	PageSizeRequestParam string

	// PageSizeValue is the value sent as PageSizeRequestParam. Values ≤ 0
	// mean "do not send the page-size parameter".
	PageSizeValue int
}

// BatchSpec configures chunked batch reads for Engine.BatchRead.
type BatchSpec struct {
	// Method is the HTTP method used for each chunk's request. Must be a
	// body-bearing method — NOT GET or HEAD (Engine.Do discards the request
	// body for those methods, which would silently drop chunk IDs). DELETE,
	// PATCH, POST, and PUT are all supported per Engine.Do's contract.
	// TRACE and OPTIONS are technically accepted but unusual for batch reads.
	Method string

	// RelativePath is appended to the base endpoint URL for each chunk request.
	RelativePath string

	// CredentialRef is the DeveloperName of the NamedCredential to use.
	CredentialRef string

	// EndpointClass is the logical endpoint group forwarded to the throttler.
	EndpointClass string

	// MaxBatchSize is the maximum number of IDs per request chunk. Must be ≥ 1.
	MaxBatchSize int

	// IDsRequestField is the top-level JSON key in the request body that holds
	// the array of IDs (e.g. "ids" → {"ids": ["id1","id2",...]}).
	IDsRequestField string

	// ExtraHeaders are added to every batch chunk request before applyAuthorization
	// runs. Used by AuthTypeCustom callers to inject the vendor-specific
	// Authorization header (or any other header) that the engine does not set.
	// Keys and values are copied; the map itself is not retained.
	ExtraHeaders http.Header

	// QueryParams are query-string parameters appended to each batch chunk
	// request. BatchSpec owns query params because RelativePath cannot carry
	// "?" (CalloutRequest rejects it at preflight). Use this for batch
	// endpoints that require versioning, projection selection, or similar
	// metadata in the query string (e.g. {"version": ["2.0"]}).
	QueryParams url.Values

	// InactiveProbeQueryParams are overlaid on QueryParams for a bounded
	// single-source confirmation read when an ordinary batch read does not
	// return that source record. A successful probe that returns the requested
	// record lets the inbound worker settle the event as skipped; an empty or
	// failed probe is not an inactivity signal. Pack configuration owns both the
	// parameter names and values so this L0 mechanism remains provider-neutral.
	InactiveProbeQueryParams url.Values `json:"inactiveProbeQueryParams,omitempty"`

	// RecordsResponsePath is the dotted JSON path to the returned records array
	// in each batch response body. Empty means the top-level response is the
	// records array itself.
	RecordsResponsePath string

	// IDResponseField is the dotted JSON path inside each returned record whose
	// value is the source-ID key used to demux the batch response. Required when
	// BatchSpec is configured; response-ID demux is the default.
	IDResponseField string

	// AllowCanonicalIDAliases allows a batch response record whose IDResponseField
	// value differs from the requested ID to satisfy that requested ID by request
	// order. Use only for APIs whose batch-read contract returns one record per
	// found requested ID in request order while canonicalizing merged aliases.
	AllowCanonicalIDAliases bool `json:"allowCanonicalIdAliases,omitempty"`

	// ExtraBodyFields are additional top-level JSON fields merged into each chunk
	// request body alongside IDsRequestField. Useful for vendor APIs that require
	// extra parameters in the POST body (e.g. a "properties" projection list).
	// Values must be valid pre-marshaled JSON. Keys that collide with IDsRequestField
	// are silently ignored to avoid overwriting the IDs array.
	ExtraBodyFields map[string]json.RawMessage `json:"extraBodyFields,omitempty"`

	// ProjectionBodyField names an ExtraBodyFields key whose JSON array receives
	// source fields required by installed inbound connector_field_mapping rows.
	// The connector pack owns the vendor request-body key; companion/app packs own
	// the field inventory through their mappings.
	ProjectionBodyField string `json:"projectionBodyField,omitempty"`

	// RecordFlattenKey, when non-empty, names a nested object key in each batch
	// response record whose entries are promoted to the top level of the record
	// (so field-mapping sourceField lookups resolve without dotted paths). The
	// nested key itself is removed after promotion. Pack-declared; L0 stays blind
	// to which vendor uses which key.
	RecordFlattenKey string `json:"recordFlattenKey,omitempty"`
}
