package models

import (
	"encoding/json"
	"time"
)

// HydrateMetadata unmarshals MetadataJSON (from DB) into Metadata (typed struct
// for Go-side access) and stores the raw JSON in MetadataRaw (for lossless API
// responses).  Must be called after reading from the database.
func (v *FlowVersion) HydrateMetadata() {
	if v.MetadataJSON == "" {
		v.MetadataRaw = json.RawMessage("{}")
		return
	}
	v.MetadataRaw = json.RawMessage(v.MetadataJSON)
	_ = json.Unmarshal([]byte(v.MetadataJSON), &v.Metadata)
}

// SerializeMetadata converts metadata into MetadataJSON for DB storage.
// It prefers MetadataRaw (populated from inbound JSON requests) to avoid
// data loss from the typed struct roundtrip.  It also syncs both
// representations so Go-side code can access the typed Metadata struct.
// Must be called before writing to the database.
func (v *FlowVersion) SerializeMetadata() {
	if len(v.MetadataRaw) > 0 && string(v.MetadataRaw) != "{}" {
		v.MetadataJSON = string(v.MetadataRaw)
		_ = json.Unmarshal(v.MetadataRaw, &v.Metadata)
		return
	}
	data, err := json.Marshal(v.Metadata)
	if err != nil {
		return
	}
	v.MetadataJSON = string(data)
	v.MetadataRaw = json.RawMessage(v.MetadataJSON)
}

// ============================================================================
// Salesforce Flow API Types
// These types match Salesforce's wire-protocol for Flow APIs
// ============================================================================

// Flow status constants
const (
	FlowStatusActive = "Active"
	FlowStatusDraft  = "Draft"
)

// FlowDefinition represents a Salesforce FlowDefinition object
// Queried via Tooling API: SELECT ... FROM FlowDefinition
type FlowDefinition struct {
	Id                   string `json:"Id" db:"id"`
	DeveloperName        string `json:"DeveloperName" db:"developer_name"`
	MasterLabel          string `json:"MasterLabel" db:"master_label"`
	Description          string `json:"Description,omitempty" db:"description"`
	ProcessType          string `json:"ProcessType" db:"process_type"`                      // AutoLaunchedFlow, Workflow, CustomEvent, etc.
	TriggerType          string `json:"TriggerType,omitempty" db:"trigger_type"`            // RecordBeforeSave, RecordAfterSave, Scheduled, PlatformEvent
	TriggerObjectOrEvent string `json:"TriggerObjectOrEvent,omitempty" db:"trigger_object"` // Object API name for record-triggered flows
	ActiveVersionId      string `json:"ActiveVersionId,omitempty" db:"active_version_id"`   // Currently active version
	LatestVersionId      string `json:"LatestVersionId,omitempty" db:"latest_version_id"`   // Most recent version
	IsDeleted            bool   `json:"-" db:"is_deleted"`
	IsCustomized         bool   `json:"-" db:"is_customized"`
	CreatedDate          string `json:"CreatedDate,omitempty" db:"created_at"`
	LastModifiedDate     string `json:"LastModifiedDate,omitempty" db:"updated_at"`
}

// FlowDefinitionPatch models the sparse Tooling PATCH surface for
// admin-authored definition edits. Internal reconcile state such as
// IsDeleted/IsCustomized is intentionally excluded.
type FlowDefinitionPatch struct {
	DeveloperName        *string `json:"DeveloperName,omitempty"`
	MasterLabel          *string `json:"MasterLabel,omitempty"`
	Description          *string `json:"Description,omitempty"`
	ProcessType          *string `json:"ProcessType,omitempty"`
	TriggerType          *string `json:"TriggerType,omitempty"`
	TriggerObjectOrEvent *string `json:"TriggerObjectOrEvent,omitempty"`
	ActiveVersionId      *string `json:"ActiveVersionId,omitempty"`
	LatestVersionId      *string `json:"LatestVersionId,omitempty"`
}

func (p *FlowDefinitionPatch) Empty() bool {
	return p == nil ||
		(p.DeveloperName == nil &&
			p.MasterLabel == nil &&
			p.Description == nil &&
			p.ProcessType == nil &&
			p.TriggerType == nil &&
			p.TriggerObjectOrEvent == nil &&
			p.ActiveVersionId == nil &&
			p.LatestVersionId == nil)
}

// FlowVersion represents a specific version of a flow (Salesforce Flow object in Tooling API)
// Queried via Tooling API: SELECT ... FROM Flow WHERE DefinitionId = '...'
type FlowVersion struct {
	Id                     string          `json:"Id" db:"id"`
	DefinitionId           string          `json:"DefinitionId" db:"definition_id"`
	MasterLabel            string          `json:"MasterLabel,omitempty" db:"master_label"`
	VersionNumber          int             `json:"VersionNumber" db:"version_number"`
	Status                 string          `json:"Status" db:"status"` // Active, Draft, Obsolete, InvalidDraft
	Description            string          `json:"Description,omitempty" db:"description"`
	ProcessType            string          `json:"ProcessType" db:"process_type"`
	Source                 string          `json:"-" db:"source"`
	DefinitionIsCustomized bool            `json:"-" db:"-"`
	IsDeleted              bool            `json:"-" db:"is_deleted"`
	Metadata               FlowMetadata    `json:"-" db:"-"`        // Typed struct for Go-side access (executor, triggers)
	MetadataJSON           string          `json:"-" db:"metadata"` // Raw JSON string for DB storage
	MetadataRaw            json.RawMessage `json:"Metadata" db:"-"` // Raw JSON for lossless API response
	CreatedDate            string          `json:"CreatedDate,omitempty" db:"created_at"`
	LastModifiedDate       string          `json:"LastModifiedDate,omitempty" db:"updated_at"`
}

// FlowMetadata contains the flow canvas structure
type FlowMetadata struct {
	// ApiVersion is the Salesforce Flow API version the canvas was built
	// against. Salesforce writes this as a JSON number in live flow metadata
	// (e.g. 57, 63.0), but older NexusCRM fixtures committed before
	// Phase 3 used the string form ("63.0"). We accept either shape by
	// typing as interface{} — the engine does not branch on this field at
	// runtime (grep confirms: nothing reads FlowMetadata.ApiVersion), it
	// is preserved solely for roundtrip fidelity with Salesforce
	// Metadata. A stricter shape would fail HydrateMetadata silently
	// (it swallows unmarshal errors) and leave every flow with an empty
	// ApiVersion — which is how this bug hid in production until Phase 3
	// tests surfaced it.
	ApiVersion  interface{} `json:"apiVersion,omitempty"`
	Status      string      `json:"status,omitempty"`
	Label       string      `json:"label,omitempty"`
	Description string      `json:"description,omitempty"`
	// RunAtomically opts a flow into single-transaction execution: every
	// RecordCreate/RecordUpdate/RecordDelete the flow performs commits or
	// rolls back as one unit. Required for invocable actions that must
	// preserve cross-object atomicity (e.g. Lead Convert). Default false
	// preserves the existing non-atomic semantics for record-triggered and
	// user-authored custom flows. Atomic flows may not contain Wait
	// elements or emailAlert action calls — either would hold the database
	// transaction open across external I/O (pause or SMTP send). Enforced
	// at execution entry for both self-declared atomic flows and subflows
	// running under an inherited atomic parent boundary (see
	// validateAtomicFlowForExecution).
	RunAtomically         bool               `json:"runAtomically,omitempty"`
	Start                 *FlowStart         `json:"start,omitempty"`
	StartElementReference string             `json:"startElementReference,omitempty"`
	Variables             []FlowVariable     `json:"variables,omitempty"`
	Decisions             []FlowDecision     `json:"decisions,omitempty"`
	Assignments           []FlowAssignment   `json:"assignments,omitempty"`
	RecordCreates         []FlowRecordCreate `json:"recordCreates,omitempty"`
	RecordUpdates         []FlowRecordUpdate `json:"recordUpdates,omitempty"`
	RecordDeletes         []FlowRecordDelete `json:"recordDeletes,omitempty"`
	RecordLookups         []FlowRecordLookup `json:"recordLookups,omitempty"`
	ActionCalls           []FlowActionCall   `json:"actionCalls,omitempty"`
	Subflows              []FlowSubflow      `json:"subflows,omitempty"`
	Loops                 []FlowLoop         `json:"loops,omitempty"`
	Screens               []FlowScreen       `json:"screens,omitempty"`
	CustomErrors          []FlowCustomError  `json:"customErrors,omitempty"`
	Waits                 []FlowWait         `json:"waits,omitempty"`
	// Resource types — preserved for roundtrip fidelity with Salesforce Metadata
	Formulas      []FlowFormula      `json:"formulas,omitempty"`
	Constants     []FlowConstant     `json:"constants,omitempty"`
	TextTemplates []FlowTextTemplate `json:"textTemplates,omitempty"`
	Choices       []FlowChoice       `json:"choices,omitempty"`
	Stages        []FlowStage        `json:"stages,omitempty"`
}

// FlowWait represents a Wait element that pauses the flow until one of the
// configured WaitEvents fires. Salesforce uses Wait elements for time-delayed
// actions — e.g. "send reminder 30 days before contract expiration".
//
// Semantics (matching Salesforce Metadata API):
//   - At execution time each WaitEvent is evaluated. If exactly one is ready
//     now (non-time-based and its conditions pass, or time-based with its
//     target already elapsed), the flow takes that event's connector.
//   - Otherwise (all pending events are time-based and none have elapsed)
//     the interview is persisted with InterviewStatus=Paused and
//     PausedUntil set to the earliest WaitEvent's target time. The
//     scheduler resumes it when PausedUntil <= NOW().
//   - If no event can ever fire, DefaultConnector is taken; on malformed
//     input the FaultConnector is taken.
type FlowWait struct {
	Name             string          `json:"name"`
	Label            string          `json:"label,omitempty"`
	Description      string          `json:"description,omitempty"`
	LocationX        int             `json:"locationX,omitempty"`
	LocationY        int             `json:"locationY,omitempty"`
	WaitEvents       []FlowWaitEvent `json:"waitEvents,omitempty"`
	DefaultConnector *FlowConnector  `json:"defaultConnector,omitempty"`
	FaultConnector   *FlowConnector  `json:"faultConnector,omitempty"`
}

// FlowWaitEvent is a single resume condition in a Wait element. EventType
// distinguishes time-based waits (e.g. "AlarmEvent", "DateRefAlarmEvent")
// — which trigger on wall-clock time — from non-time-based waits whose
// conditions must already evaluate true at execution time.
type FlowWaitEvent struct {
	Name            string                `json:"name,omitempty"`
	Label           string                `json:"label,omitempty"`
	ConditionLogic  string                `json:"conditionLogic,omitempty"`
	Conditions      []FlowCondition       `json:"conditions,omitempty"`
	Connector       *FlowConnector        `json:"connector,omitempty"`
	EventType       string                `json:"eventType,omitempty"`
	InputParameters []FlowActionParameter `json:"inputParameters,omitempty"`
}

// FlowStart defines flow entry point and trigger conditions
type FlowStart struct {
	LocationX         int                 `json:"locationX"`
	LocationY         int                 `json:"locationY"`
	Connector         *FlowConnector      `json:"connector,omitempty"`
	Object            string              `json:"object,omitempty"`            // For record-triggered flows
	TriggerType       string              `json:"triggerType,omitempty"`       // RecordBeforeSave, RecordAfterSave, etc.
	RecordTriggerType string              `json:"recordTriggerType,omitempty"` // Create, Update, CreateAndUpdate, Delete
	FilterLogic       string              `json:"filterLogic,omitempty"`
	Filters           []FlowRecordFilter  `json:"filters,omitempty"`
	ScheduledPaths    []FlowScheduledPath `json:"scheduledPaths,omitempty"`
	Schedule          *FlowSchedule       `json:"schedule,omitempty"`
}

// FlowConnector links flow elements
type FlowConnector struct {
	TargetReference string `json:"targetReference"`
}

// FlowVariable stores flow state
type FlowVariable struct {
	Name         string      `json:"name"`
	DataType     string      `json:"dataType"` // String, Number, Boolean, Date, DateTime, Currency, SObject, etc.
	IsCollection bool        `json:"isCollection,omitempty"`
	IsInput      bool        `json:"isInput,omitempty"`
	IsOutput     bool        `json:"isOutput,omitempty"`
	Value        interface{} `json:"value,omitempty"`
	ObjectType   string      `json:"objectType,omitempty"` // For SObject variables
}

// FlowDecision represents a decision element with multiple outcomes
type FlowDecision struct {
	Name                  string             `json:"name"`
	Label                 string             `json:"label,omitempty"`
	Description           string             `json:"description,omitempty"`
	LocationX             int                `json:"locationX"`
	LocationY             int                `json:"locationY"`
	Rules                 []FlowDecisionRule `json:"rules,omitempty"`
	DefaultConnector      *FlowConnector     `json:"defaultConnector,omitempty"`
	DefaultConnectorLabel string             `json:"defaultConnectorLabel,omitempty"`
}

type FlowDecisionRule struct {
	Name           string          `json:"name"`
	Label          string          `json:"label,omitempty"`
	ConditionLogic string          `json:"conditionLogic,omitempty"` // and, or, custom
	Conditions     []FlowCondition `json:"conditions,omitempty"`
	Connector      *FlowConnector  `json:"connector,omitempty"`
}

type FlowCondition struct {
	LeftValueReference string      `json:"leftValueReference"`
	Operator           string      `json:"operator"` // EqualTo, NotEqualTo, GreaterThan, Contains, etc.
	RightValue         interface{} `json:"rightValue,omitempty"`
}

// FlowAssignment modifies flow variables
type FlowAssignment struct {
	Name        string               `json:"name"`
	Label       string               `json:"label,omitempty"`
	Description string               `json:"description,omitempty"`
	LocationX   int                  `json:"locationX"`
	LocationY   int                  `json:"locationY"`
	Items       []FlowAssignmentItem `json:"assignmentItems,omitempty"`
	Connector   *FlowConnector       `json:"connector,omitempty"`
}

type FlowAssignmentItem struct {
	AssignToReference string      `json:"assignToReference"`
	Operator          string      `json:"operator"` // Assign, Add, Subtract, etc.
	Value             interface{} `json:"value,omitempty"`
}

// FlowRecordCreate creates a record
type FlowRecordCreate struct {
	Name                      string                `json:"name"`
	Label                     string                `json:"label,omitempty"`
	LocationX                 int                   `json:"locationX"`
	LocationY                 int                   `json:"locationY"`
	Object                    string                `json:"object"`
	InputAssignments          []FlowInputAssignment `json:"inputAssignments,omitempty"`
	StoreOutputAutomatically  bool                  `json:"storeOutputAutomatically,omitempty"`
	AssignRecordIdToReference string                `json:"assignRecordIdToReference,omitempty"`
	// FieldMapping applies rows from the _System_ObjectFieldMapping registry
	// to the new record before InputAssignments run, letting admins drive
	// object-to-object transforms (e.g. Lead→Contact on Lead Convert) as
	// data. InputAssignments always override mapped values, so a Flow
	// author can opt in to the registry yet still pin specific fields.
	FieldMapping   *FlowFieldMappingRef `json:"fieldMapping,omitempty"`
	Connector      *FlowConnector       `json:"connector,omitempty"`
	FaultConnector *FlowConnector       `json:"faultConnector,omitempty"`
}

// FlowFieldMappingRef references a group of rows in _System_ObjectFieldMapping
// keyed by (SourceObject, TargetObject, ActionType). The Flow executor
// resolves SourceReference from the interview context to locate the source
// SObject at runtime.
type FlowFieldMappingRef struct {
	SourceObject    string `json:"sourceObject"`
	SourceReference string `json:"sourceReference"`
	TargetObject    string `json:"targetObject,omitempty"`
	TargetReference string `json:"targetReference,omitempty"`
	ActionType      string `json:"actionType"`
	MergeMode       string `json:"mergeMode,omitempty"`
}

// FlowRecordUpdate updates records
type FlowRecordUpdate struct {
	Name             string                `json:"name"`
	Label            string                `json:"label,omitempty"`
	LocationX        int                   `json:"locationX"`
	LocationY        int                   `json:"locationY"`
	Object           string                `json:"object,omitempty"`
	InputReference   string                `json:"inputReference,omitempty"` // Reference to SObject variable
	InputAssignments []FlowInputAssignment `json:"inputAssignments,omitempty"`
	FieldMapping     *FlowFieldMappingRef  `json:"fieldMapping,omitempty"`
	FilterLogic      string                `json:"filterLogic,omitempty"`
	Filters          []FlowRecordFilter    `json:"filters,omitempty"`
	Connector        *FlowConnector        `json:"connector,omitempty"`
	FaultConnector   *FlowConnector        `json:"faultConnector,omitempty"`
}

// FlowRecordDelete deletes records
type FlowRecordDelete struct {
	Name           string             `json:"name"`
	Label          string             `json:"label,omitempty"`
	LocationX      int                `json:"locationX"`
	LocationY      int                `json:"locationY"`
	Object         string             `json:"object,omitempty"`
	InputReference string             `json:"inputReference,omitempty"`
	FilterLogic    string             `json:"filterLogic,omitempty"`
	Filters        []FlowRecordFilter `json:"filters,omitempty"`
	Connector      *FlowConnector     `json:"connector,omitempty"`
	FaultConnector *FlowConnector     `json:"faultConnector,omitempty"`
}

// FlowRecordLookup queries records
type FlowRecordLookup struct {
	Name                     string                 `json:"name"`
	Label                    string                 `json:"label,omitempty"`
	Description              string                 `json:"description,omitempty"`
	LocationX                int                    `json:"locationX"`
	LocationY                int                    `json:"locationY"`
	Object                   string                 `json:"object"`
	FilterLogic              string                 `json:"filterLogic,omitempty"`
	Filters                  []FlowRecordFilter     `json:"filters,omitempty"`
	OutputAssignments        []FlowOutputAssignment `json:"outputAssignments,omitempty"`
	OutputReference          string                 `json:"outputReference,omitempty"`
	StoreOutputAutomatically bool                   `json:"storeOutputAutomatically,omitempty"`
	GetFirstRecordOnly       bool                   `json:"getFirstRecordOnly,omitempty"`
	SortOrder                string                 `json:"sortOrder,omitempty"`
	SortField                string                 `json:"sortField,omitempty"`
	Connector                *FlowConnector         `json:"connector,omitempty"`
	FaultConnector           *FlowConnector         `json:"faultConnector,omitempty"`
}

// FlowProcessMetadataValue is a Salesforce Metadata API annotation carried
// by Process Builder-generated flow elements. The engine uses it to detect
// the "implicit-Id" pattern on RecordUpdate/RecordDelete filters.
type FlowProcessMetadataValue struct {
	Name  string      `json:"name"`
	Value interface{} `json:"value,omitempty"`
}

type FlowRecordFilter struct {
	Field                 string                     `json:"field"`
	Operator              string                     `json:"operator"`
	Value                 interface{}                `json:"value,omitempty"`
	ProcessMetadataValues []FlowProcessMetadataValue `json:"processMetadataValues,omitempty"`
}

type FlowInputAssignment struct {
	Field string      `json:"field"`
	Value interface{} `json:"value,omitempty"`
}

type FlowOutputAssignment struct {
	AssignToReference string `json:"assignToReference"`
	Field             string `json:"field"`
}

// FlowActionCall invokes an action (Apex, Flow, Quick Action, etc.)
type FlowActionCall struct {
	Name             string                `json:"name"`
	Label            string                `json:"label,omitempty"`
	LocationX        int                   `json:"locationX"`
	LocationY        int                   `json:"locationY"`
	ActionName       string                `json:"actionName"`
	ActionType       string                `json:"actionType"` // apex, flow, quickAction, emailAlert, etc.
	InputParameters  []FlowActionParameter `json:"inputParameters,omitempty"`
	OutputParameters []FlowActionParameter `json:"outputParameters,omitempty"`
	Connector        *FlowConnector        `json:"connector,omitempty"`
	FaultConnector   *FlowConnector        `json:"faultConnector,omitempty"`
}

type FlowActionParameter struct {
	Name  string      `json:"name"`
	Value interface{} `json:"value,omitempty"`
}

// FlowSubflow calls another flow
type FlowSubflow struct {
	Name              string              `json:"name"`
	Label             string              `json:"label,omitempty"`
	LocationX         int                 `json:"locationX"`
	LocationY         int                 `json:"locationY"`
	FlowName          string              `json:"flowName"`
	InputAssignments  []FlowSubflowInput  `json:"inputAssignments,omitempty"`
	OutputAssignments []FlowSubflowOutput `json:"outputAssignments,omitempty"`
	Connector         *FlowConnector      `json:"connector,omitempty"`
	FaultConnector    *FlowConnector      `json:"faultConnector,omitempty"`
}

type FlowSubflowInput struct {
	Name  string      `json:"name"`
	Value interface{} `json:"value,omitempty"`
}

type FlowSubflowOutput struct {
	AssignToReference string `json:"assignToReference"`
	Name              string `json:"name"`
}

// FlowLoop iterates over a collection
type FlowLoop struct {
	Name                       string         `json:"name"`
	Label                      string         `json:"label,omitempty"`
	LocationX                  int            `json:"locationX"`
	LocationY                  int            `json:"locationY"`
	CollectionReference        string         `json:"collectionReference"`                  // Variable containing the collection
	IterationOrder             string         `json:"iterationOrder,omitempty"`             // Asc, Desc
	AssignNextValueToReference string         `json:"assignNextValueToReference,omitempty"` // Variable to store current item
	NextValueConnector         *FlowConnector `json:"nextValueConnector,omitempty"`         // Loop body
	NoMoreValuesConnector      *FlowConnector `json:"noMoreValuesConnector,omitempty"`      // Exit path
}

// FlowScreen represents a screen flow element (for user interaction)
type FlowScreen struct {
	Name      string            `json:"name"`
	Label     string            `json:"label,omitempty"`
	LocationX int               `json:"locationX"`
	LocationY int               `json:"locationY"`
	Fields    []FlowScreenField `json:"fields,omitempty"`
	Connector *FlowConnector    `json:"connector,omitempty"`
}

type FlowScreenField struct {
	Name         string      `json:"name"`
	FieldType    string      `json:"fieldType"`
	DataType     string      `json:"dataType,omitempty"`
	IsRequired   bool        `json:"isRequired,omitempty"`
	DefaultValue interface{} `json:"defaultValue,omitempty"`
}

// FlowSchedule defines scheduled flow execution
type FlowSchedule struct {
	Frequency      string `json:"frequency"` // Once, Daily, Weekly
	StartDate      string `json:"startDate,omitempty"`
	StartTime      string `json:"startTime,omitempty"`
	TimezoneSidKey string `json:"timezoneSidKey,omitempty"` // e.g. "UTC", "America/New_York"
}

type FlowScheduledPath struct {
	Name         string         `json:"name"`
	Label        string         `json:"label,omitempty"`
	Connector    *FlowConnector `json:"connector,omitempty"`
	TimeSource   string         `json:"timeSource,omitempty"` // RecordField
	OffsetNumber int            `json:"offsetNumber,omitempty"`
	OffsetUnit   string         `json:"offsetUnit,omitempty"` // Hours, Days
}

// FlowCustomError represents a custom error element that blocks record saves
type FlowCustomError struct {
	Name                string                   `json:"name"`
	Label               string                   `json:"label,omitempty"`
	Description         string                   `json:"description,omitempty"`
	LocationX           int                      `json:"locationX"`
	LocationY           int                      `json:"locationY"`
	CustomErrorMessages []FlowCustomErrorMessage `json:"customErrorMessages,omitempty"`
	Connector           *FlowConnector           `json:"connector,omitempty"`
}

// FlowCustomErrorMessage defines the error message details
//
// StatusCode carries the Salesforce-native error code the dispatcher should
// surface in FlowExecutionResult.Errors[].StatusCode (e.g. "ALREADY_CONVERTED",
// "MALFORMED_ID", "FIELD_CUSTOM_VALIDATION_EXCEPTION"). Absent → the dispatcher
// falls back to the generic "FLOW_ERROR" sentinel. Lets flows raise
// SF-API-contract-compatible errors without a Go code change.
type FlowCustomErrorMessage struct {
	ErrorMessage   string `json:"errorMessage"`
	StatusCode     string `json:"statusCode,omitempty"`
	FieldSelection string `json:"fieldSelection,omitempty"`
	IsFieldError   bool   `json:"isFieldError,omitempty"`
}

// ============================================================================
// Flow Resource Types (for roundtrip fidelity with Salesforce Metadata)
// ============================================================================

// FlowFormula represents a formula resource in a flow
type FlowFormula struct {
	Name        string `json:"name"`
	Description string `json:"description,omitempty"`
	DataType    string `json:"dataType"`
	Expression  string `json:"expression"`
	Scale       int    `json:"scale,omitempty"`
}

// FlowConstant represents a constant resource in a flow
type FlowConstant struct {
	Name        string      `json:"name"`
	Description string      `json:"description,omitempty"`
	DataType    string      `json:"dataType"`
	Value       interface{} `json:"value,omitempty"`
}

// FlowTextTemplate represents a text template resource in a flow
type FlowTextTemplate struct {
	Name                string `json:"name"`
	Description         string `json:"description,omitempty"`
	Text                string `json:"text"`
	IsViewedAsPlainText bool   `json:"isViewedAsPlainText,omitempty"`
}

// FlowChoice represents a choice resource in a flow
type FlowChoice struct {
	Name        string      `json:"name"`
	Description string      `json:"description,omitempty"`
	ChoiceText  string      `json:"choiceText"`
	DataType    string      `json:"dataType"`
	Value       interface{} `json:"value,omitempty"`
}

// FlowStage represents a stage in a flow
type FlowStage struct {
	Name        string `json:"name"`
	Description string `json:"description,omitempty"`
	Label       string `json:"label"`
	IsActive    bool   `json:"isActive,omitempty"`
	StageOrder  int    `json:"stageOrder,omitempty"`
}

// ============================================================================
// Flow Interview (Runtime Instance)
// ============================================================================

// FlowInterview represents a running/paused flow instance
// Queried via sObject API: SELECT ... FROM FlowInterview
type FlowInterview struct {
	Id              string     `json:"Id" db:"id"`
	FlowVersionId   string     `json:"FlowVersionId" db:"flow_version_id"`
	InterviewLabel  string     `json:"InterviewLabel,omitempty" db:"interview_label"`
	InterviewStatus string     `json:"InterviewStatus" db:"interview_status"` // Paused, Running, Error, Finished
	CurrentElement  string     `json:"CurrentElement,omitempty" db:"current_element"`
	OwnerId         string     `json:"OwnerId" db:"owner_id"`
	CreatedDate     time.Time  `json:"CreatedDate" db:"created_at"`
	PausedDate      *time.Time `json:"PausedDate,omitempty" db:"paused_date"`
	// PausedUntil is set when the interview is paused inside a Wait element.
	// Distinct from PausedDate (which records *when* it was paused):
	// PausedUntil records *when to wake up*. The scheduler polls
	//   SELECT Id FROM FlowInterview WHERE InterviewStatus='Paused' AND PausedUntil <= NOW()
	// (backed by the composite index idx_flow_interview_paused_scan) and
	// calls ResumeInterview on each match.
	PausedUntil *time.Time `json:"PausedUntil,omitempty" db:"paused_until"`
	// ContextData stores flow variable values during execution
	ContextData     map[string]interface{} `json:"-" db:"-"`
	ContextDataJSON string                 `json:"-" db:"context_data"`
	// IsTransient marks an interview that exists only in memory for the
	// duration of a single synchronous run. The flow executor uses this
	// for record-triggered flows that do not pause: their interview row
	// has no audit value (it would be inserted as Running then immediately
	// updated to Finished with no intervening state), and the two extra
	// DB round-trips dominate save latency when a record has many flows
	// (e.g. Lead has ~24 before-save flows → ~160s on remote TiDB Cloud).
	// Salesforce itself defaults to not storing finished record-triggered
	// interviews — see Process Automation Settings → "Store Flow Interviews".
	// This field is in-memory only and never serialized or persisted.
	IsTransient bool `json:"-" db:"-"`
}

// FlowInterview status constants
const (
	FlowInterviewStatusRunning  = "Running"
	FlowInterviewStatusPaused   = "Paused"
	FlowInterviewStatusFinished = "Finished"
	FlowInterviewStatusError    = "Error"
)

// FlowInstanceProgress provides progress info for approval workflows
type FlowInstanceProgress struct {
	InstanceId     string `json:"instance_id"`
	FlowName       string `json:"flow_name"`
	CurrentElement string `json:"current_element"`
	Status         string `json:"status"`
	CompletedSteps int    `json:"completed_steps"`
	TotalSteps     int    `json:"total_steps"`
}

// ============================================================================
// Actions API Types
// ============================================================================

// InvocableFlow represents a flow listed via Actions API
// GET /services/data/v63.0/actions/custom/flow
type InvocableFlow struct {
	Label       string            `json:"label"`
	Name        string            `json:"name"`
	Type        string            `json:"type"` // FLOW
	Url         string            `json:"url"`
	Description string            `json:"description,omitempty"`
	Inputs      []FlowInputOutput `json:"inputs,omitempty"`
	Outputs     []FlowInputOutput `json:"outputs,omitempty"`
}

// FlowInputOutput describes flow input/output parameters
type FlowInputOutput struct {
	Name        string `json:"name"`
	Label       string `json:"label,omitempty"`
	DataType    string `json:"dataType"`
	Required    bool   `json:"required,omitempty"`
	Description string `json:"description,omitempty"`
}

// FlowExecutionRequest represents input for flow execution
// POST /services/data/v63.0/actions/custom/flow/:name
type FlowExecutionRequest struct {
	Inputs []map[string]interface{} `json:"inputs"`
}

// FlowExecutionResult represents the result of flow execution
type FlowExecutionResult struct {
	ActionName   string                 `json:"actionName"`
	IsSuccess    bool                   `json:"isSuccess"`
	Errors       []FlowExecutionError   `json:"errors,omitempty"`
	OutputValues map[string]interface{} `json:"outputValues,omitempty"`
}

// FlowExecutionError represents an error during flow execution
type FlowExecutionError struct {
	StatusCode string   `json:"statusCode"`
	Message    string   `json:"message"`
	Fields     []string `json:"fields,omitempty"`
}

// ============================================================================
// Tooling API Query Response Types
// ============================================================================

// ToolingQueryResponse wraps Tooling API query results
type ToolingQueryResponse[T any] struct {
	Size         int    `json:"size"`
	TotalSize    int    `json:"totalSize"`
	Done         bool   `json:"done"`
	QueryLocator string `json:"queryLocator,omitempty"`
	Records      []T    `json:"records"`
}
