package dialectsfcrm

import (
	"context"
	"encoding/json"
	"fmt"
	"sort"
	"strings"
	"time"

	cp "github.com/pingcap-inc/nexuscrm-contract/pkg/controlplane"
)

const (
	approvalProcessManagedTargetDomain    = "approval_process/developer_name/utf8mb4_unicode_ci-v1"
	approvalProcessManagedSetTargetDomain = "approval_process/owner_pack/exact_set-v1"
	approvalProcessManagedMaximumRows     = 500
)

type approvalProcessManagedOpaquePlan struct {
	Scope       cp.ComponentManagedScope `json:"scope"`
	PackName    string                   `json:"pack_name"`
	BusinessKey string                   `json:"business_key,omitempty"`
	Retirements int                      `json:"retirements,omitempty"`
}

type approvalProcessManagedSetProjection struct {
	BusinessKeys []string `json:"business_keys"`
}

// NewApprovalProcessManagedLifecycleRegistration exposes the additive managed
// lifecycle without removing the legacy five-callback registration. A host
// selects exactly one registration path for this component kind.
func NewApprovalProcessManagedLifecycleRegistration() (cp.ComponentKindRegistration, cp.ComponentManagedLifecycleRegistration, error) {
	base, err := NewApprovalProcessComponentRegistration()
	if err != nil {
		return cp.ComponentKindRegistration{}, cp.ComponentManagedLifecycleRegistration{}, err
	}
	base.ManagedProvider = nil
	lifecycle, err := cp.NewComponentManagedLifecycleRegistration(cp.ComponentManagedLifecycleRegistrationSpec{
		Kind: cp.ComponentKindApprovalProcess,
		ReadSet: []cp.ComponentReadSurface{
			{Table: approvalProcessDefinitionTable, Fact: "approval process definitions and exact owner set"},
			{Table: approvalProcessNodeTable, Fact: "approval process steps"},
			{Table: approvalProcessActionTable, Fact: "approval process actions"},
		},
		Capabilities: []cp.ComponentManagedCapability{
			cp.ComponentManagedCapabilityExactSet,
			cp.ComponentManagedCapabilityTargetSerialization,
			cp.ComponentManagedCapabilityStructuredMutation,
			cp.ComponentManagedCapabilityEvidence,
			cp.ComponentManagedCapabilityFreshProjection,
		},
		TransactionMode: cp.ComponentManagedTransactionPessimisticReadCommitted,
		Budget: cp.ComponentManagedBudget{
			MaxTargets:            1001,
			MaxCurrentItems:       2500,
			MaxDesiredItems:       approvalProcessManagedMaximumRows,
			MaxDeltaRows:          2501,
			MaxMutationBytes:      16 << 20,
			MaxMutationStatements: 2000,
			MaxBatchRows:          approvalProcessManagedMaximumRows,
			MaxEvidenceWrites:     1,
			MaxEffectCalls:        1,
			MaxLockWait:           5 * time.Second,
			MaxElapsed:            30 * time.Second,
			MaxAttempts:           3,
		},
		Lifecycle: cp.ComponentManagedLifecycleProvider{
			Plan: approvalProcessManagedLifecyclePlan, Mutate: approvalProcessManagedLifecycleMutate,
			Project: approvalProcessManagedLifecycleProject,
		},
	})
	if err != nil {
		return cp.ComponentKindRegistration{}, cp.ComponentManagedLifecycleRegistration{}, fmt.Errorf("approval process managed lifecycle: %w", err)
	}
	return base, lifecycle, nil
}

func approvalProcessManagedLifecyclePlan(ctx context.Context, scope cp.ComponentManagedScope, request cp.ComponentManagedLifecycleRequest, store cp.ComponentManagedReadStore) (cp.ComponentManagedPlan, error) {
	if store == nil {
		return cp.ComponentManagedPlan{}, fmt.Errorf("approval process managed lifecycle read store is required")
	}
	if err := request.Validate(scope); err != nil {
		return cp.ComponentManagedPlan{}, err
	}
	switch scope {
	case cp.ComponentManagedScopeItem:
		return approvalProcessManagedLifecyclePlanItem(ctx, request, store)
	case cp.ComponentManagedScopeSet:
		return approvalProcessManagedLifecyclePlanSet(ctx, request, store)
	default:
		return cp.ComponentManagedPlan{}, fmt.Errorf("approval process managed lifecycle scope %q is unsupported", scope)
	}
}

func approvalProcessManagedLifecyclePlanItem(ctx context.Context, request cp.ComponentManagedLifecycleRequest, store cp.ComponentManagedReadStore) (cp.ComponentManagedPlan, error) {
	desired, err := decodeApprovalProcessManagedRequest(*request.Item)
	if err != nil {
		return cp.ComponentManagedPlan{}, fmt.Errorf("approval process managed lifecycle plan: %w", err)
	}
	if len(desired.Nodes) > approvalProcessManagedMaximumRows {
		return cp.ComponentManagedPlan{}, fmt.Errorf("approval process %s managed lifecycle has %d desired ProcessNode rows; maximum is %d", request.Item.Component.BusinessKey, len(desired.Nodes), approvalProcessManagedMaximumRows)
	}
	if len(desired.Actions) > approvalProcessManagedMaximumRows {
		return cp.ComponentManagedPlan{}, fmt.Errorf("approval process %s managed lifecycle has %d desired ProcessAction rows; maximum is %d", request.Item.Component.BusinessKey, len(desired.Actions), approvalProcessManagedMaximumRows)
	}
	reader := &approvalProcessManagedLifecycleReader{store: store}
	if err := preflightApprovalProcessManagedComponent(ctx, reader, *request.Item); err != nil {
		return cp.ComponentManagedPlan{}, err
	}
	current := 0
	if reader.lastDefinitionRows != 0 {
		current = 1
	}
	key := desired.Parent.BusinessKey
	target, err := ApprovalProcessManagedTarget(key.DeveloperName)
	if err != nil {
		return cp.ComponentManagedPlan{}, err
	}
	opaque, err := json.Marshal(approvalProcessManagedOpaquePlan{
		Scope: cp.ComponentManagedScopeItem, PackName: request.Item.PackName, BusinessKey: request.Item.Component.BusinessKey,
	})
	if err != nil {
		return cp.ComponentManagedPlan{}, err
	}
	return cp.NewComponentManagedPlan(cp.ComponentManagedPlanSpec{
		Targets: []cp.ComponentManagedTarget{target, approvalProcessManagedExactSetTarget()},
		RequiredCapabilities: []cp.ComponentManagedCapability{
			cp.ComponentManagedCapabilityTargetSerialization,
			cp.ComponentManagedCapabilityStructuredMutation,
			cp.ComponentManagedCapabilityEvidence,
			cp.ComponentManagedCapabilityFreshProjection,
		},
		Estimate: cp.ComponentManagedUsage{
			CurrentItems: current, DesiredItems: 1,
			DeltaRows:          1001 + len(desired.Nodes) + len(desired.Actions),
			MutationStatements: 2 + 2*len(desired.Nodes) + len(desired.Actions),
		},
		ProjectionIdentity: approvalProcessManagedItemProjectionIdentity(*request.Item),
		OpaquePlan:         opaque,
	})
}

// ApprovalProcessManagedTargets is the complete target footprint shared by
// item writers and exact-set reconciliation.
func ApprovalProcessManagedTargets(developerName string) ([]cp.ComponentManagedTarget, error) {
	target, err := ApprovalProcessManagedTarget(developerName)
	if err != nil {
		return nil, err
	}
	return []cp.ComponentManagedTarget{target, approvalProcessManagedExactSetTarget()}, nil
}

func approvalProcessManagedLifecyclePlanSet(ctx context.Context, request cp.ComponentManagedLifecycleRequest, store cp.ComponentManagedReadStore) (cp.ComponentManagedPlan, error) {
	set := *request.Set
	if err := validateApprovalProcessManagedSetRequest(set); err != nil {
		return cp.ComponentManagedPlan{}, err
	}
	desired, developerObjects, err := approvalProcessManagedDesiredSet(request.DesiredBusinessKeys)
	if err != nil {
		return cp.ComponentManagedPlan{}, err
	}
	rows, err := store.ReadRows(ctx, approvalProcessManagedDefinitionSetQuery(set.PackName))
	if err != nil {
		return cp.ComponentManagedPlan{}, fmt.Errorf("read owner-scoped ProcessDefinition rows for %s: %w", set.PackName, err)
	}
	targets := []cp.ComponentManagedTarget{approvalProcessManagedExactSetTarget()}
	retirements := 0
	seen := make(map[string]struct{}, len(rows))
	for _, source := range rows {
		row := cloneApprovalProcessManagedRow(source)
		identity, developerName, retired, rowErr := approvalProcessManagedSetRow(set.PackName, row, seen, developerObjects)
		if rowErr != nil {
			return cp.ComponentManagedPlan{}, rowErr
		}
		target, targetErr := ApprovalProcessManagedTarget(developerName)
		if targetErr != nil {
			return cp.ComponentManagedPlan{}, targetErr
		}
		targets = append(targets, target)
		if _, retained := desired[identity]; !retained && !retired {
			retirements++
		}
	}
	for _, businessKey := range request.DesiredBusinessKeys {
		_, developerName, _ := splitApprovalProcessComponentBusinessKey(businessKey)
		target, targetErr := ApprovalProcessManagedTarget(developerName)
		if targetErr != nil {
			return cp.ComponentManagedPlan{}, targetErr
		}
		targets = append(targets, target)
	}
	opaque, err := json.Marshal(approvalProcessManagedOpaquePlan{Scope: cp.ComponentManagedScopeSet, PackName: set.PackName, Retirements: retirements})
	if err != nil {
		return cp.ComponentManagedPlan{}, err
	}
	return cp.NewComponentManagedPlan(cp.ComponentManagedPlanSpec{
		Targets: targets,
		RequiredCapabilities: []cp.ComponentManagedCapability{
			cp.ComponentManagedCapabilityExactSet,
			cp.ComponentManagedCapabilityTargetSerialization,
			cp.ComponentManagedCapabilityStructuredMutation,
			cp.ComponentManagedCapabilityEvidence,
			cp.ComponentManagedCapabilityFreshProjection,
		},
		Estimate: cp.ComponentManagedUsage{
			CurrentItems: len(rows), DesiredItems: len(request.DesiredBusinessKeys), DeltaRows: retirements, MutationStatements: retirements,
		},
		ProjectionIdentity: approvalProcessManagedSetProjectionIdentity(set),
		OpaquePlan:         opaque,
	})
}

func approvalProcessManagedLifecycleMutate(ctx context.Context, scope cp.ComponentManagedScope, request cp.ComponentManagedLifecycleRequest, plan cp.ComponentManagedPlan, store cp.ComponentManagedMutationStore) (cp.ComponentManagedMutationResult, error) {
	if store == nil {
		return cp.ComponentManagedMutationResult{}, fmt.Errorf("approval process managed lifecycle mutation store is required")
	}
	if err := request.Validate(scope); err != nil {
		return cp.ComponentManagedMutationResult{}, err
	}
	var opaque approvalProcessManagedOpaquePlan
	if err := json.Unmarshal(plan.OpaquePlan(), &opaque); err != nil {
		return cp.ComponentManagedMutationResult{}, fmt.Errorf("decode approval process managed lifecycle opaque plan: %w", err)
	}
	if opaque.Scope != scope {
		return cp.ComponentManagedMutationResult{}, fmt.Errorf("approval process managed lifecycle opaque scope %q does not match %q", opaque.Scope, scope)
	}
	adapter := &approvalProcessManagedLifecycleMutationStore{store: store}
	switch scope {
	case cp.ComponentManagedScopeItem:
		if opaque.PackName != request.Item.PackName || opaque.BusinessKey != request.Item.Component.BusinessKey {
			return cp.ComponentManagedMutationResult{}, fmt.Errorf("approval process item opaque plan identity does not match request")
		}
		if err := applyApprovalProcessManagedComponent(ctx, adapter, *request.Item); err != nil {
			return cp.ComponentManagedMutationResult{}, err
		}
		if err := adapter.err; err != nil {
			return cp.ComponentManagedMutationResult{}, err
		}
		return cp.ComponentManagedMutationResult{Disposition: cp.ComponentManagedMutationDesiredProjectionStaged}, nil
	case cp.ComponentManagedScopeSet:
		if opaque.PackName != request.Set.PackName {
			return cp.ComponentManagedMutationResult{}, fmt.Errorf("approval process set opaque plan identity does not match request")
		}
		if opaque.Retirements == 0 {
			return cp.ComponentManagedMutationResult{Disposition: cp.ComponentManagedMutationNoChange}, nil
		}
		if err := reconcileApprovalProcessManagedSet(ctx, adapter, *request.Set, request.DesiredBusinessKeys); err != nil {
			return cp.ComponentManagedMutationResult{}, err
		}
		if err := adapter.err; err != nil {
			return cp.ComponentManagedMutationResult{}, err
		}
		return cp.ComponentManagedMutationResult{Disposition: cp.ComponentManagedMutationDesiredProjectionStaged}, nil
	default:
		return cp.ComponentManagedMutationResult{}, fmt.Errorf("approval process managed lifecycle scope %q is unsupported", scope)
	}
}

func approvalProcessManagedLifecycleProject(ctx context.Context, scope cp.ComponentManagedScope, request cp.ComponentManagedLifecycleRequest, store cp.ComponentManagedReadStore) (cp.ComponentManagedProjection, error) {
	if store == nil {
		return cp.ComponentManagedProjection{}, fmt.Errorf("approval process managed lifecycle projection read store is required")
	}
	if err := request.Validate(scope); err != nil {
		return cp.ComponentManagedProjection{}, err
	}
	reader := &approvalProcessManagedLifecycleReader{store: store}
	switch scope {
	case cp.ComponentManagedScopeItem:
		expected, live, err := verifyApprovalProcessManagedProjection(ctx, reader, *request.Item)
		if err != nil {
			return cp.ComponentManagedProjection{}, err
		}
		return cp.ComponentManagedProjection{Identity: approvalProcessManagedItemProjectionIdentity(*request.Item), Expected: expected, Live: live}, nil
	case cp.ComponentManagedScopeSet:
		if err := validateApprovalProcessManagedSetRequest(*request.Set); err != nil {
			return cp.ComponentManagedProjection{}, err
		}
		expected, _, err := approvalProcessManagedDesiredSet(request.DesiredBusinessKeys)
		if err != nil {
			return cp.ComponentManagedProjection{}, err
		}
		rows, err := store.ReadRows(ctx, approvalProcessManagedDefinitionSetQuery(request.Set.PackName))
		if err != nil {
			return cp.ComponentManagedProjection{}, err
		}
		live := make(map[string]string, len(rows))
		seen, developerObjects := make(map[string]struct{}, len(rows)), make(map[string]string, len(rows))
		for _, source := range rows {
			row := cloneApprovalProcessManagedRow(source)
			identity, _, retired, rowErr := approvalProcessManagedSetRow(request.Set.PackName, row, seen, developerObjects)
			if rowErr != nil {
				return cp.ComponentManagedProjection{}, rowErr
			}
			if retainedKey, retained := expected[identity]; retained {
				live[identity] = retainedKey
			} else if !retired {
				objectName, _ := approvalProcessManagedProjectionString("ProcessDefinition", row, "TableEnumOrId")
				developerName, _ := approvalProcessManagedProjectionString("ProcessDefinition", row, "DeveloperName")
				live[identity] = objectName + "/" + developerName
			}
		}
		expectedBytes, err := marshalApprovalProcessManagedSetProjection(expected)
		if err != nil {
			return cp.ComponentManagedProjection{}, err
		}
		liveBytes, err := marshalApprovalProcessManagedSetProjection(live)
		if err != nil {
			return cp.ComponentManagedProjection{}, err
		}
		return cp.ComponentManagedProjection{Identity: approvalProcessManagedSetProjectionIdentity(*request.Set), Expected: expectedBytes, Live: liveBytes}, nil
	default:
		return cp.ComponentManagedProjection{}, fmt.Errorf("approval process managed lifecycle scope %q is unsupported", scope)
	}
}

func approvalProcessManagedItemProjectionIdentity(request cp.ComponentManagedRequest) []byte {
	return []byte(string(cp.ComponentKindApprovalProcess) + "\x00" + request.PackName + "\x00" + request.Component.BusinessKey)
}

// ApprovalProcessManagedTarget resolves every approval-process writer to the
// same physical ProcessDefinition.DeveloperName uniqueness target.
func ApprovalProcessManagedTarget(developerName string) (cp.ComponentManagedTarget, error) {
	if !metadataDeploySafeIdentifier.MatchString(developerName) {
		return cp.ComponentManagedTarget{}, fmt.Errorf("approval process developer name %q is not canonical", developerName)
	}
	return cp.ComponentManagedTarget{
		TargetDomain: approvalProcessManagedTargetDomain, PhysicalKeyBytes: []byte(strings.ToLower(developerName)),
	}, nil
}

func approvalProcessManagedExactSetTarget() cp.ComponentManagedTarget {
	return cp.ComponentManagedTarget{TargetDomain: approvalProcessManagedSetTargetDomain, PhysicalKeyBytes: []byte{0}}
}

func approvalProcessManagedSetProjectionIdentity(request cp.ComponentManagedSetRequest) []byte {
	return []byte(string(cp.ComponentKindApprovalProcess) + "\x00" + request.PackName + "\x00*")
}

func validateApprovalProcessManagedSetRequest(request cp.ComponentManagedSetRequest) error {
	if request.PackName == "" || strings.TrimSpace(request.PackName) != request.PackName {
		return fmt.Errorf("pack name %q is not canonical", request.PackName)
	}
	if request.SourceOwner != "" {
		return fmt.Errorf("pack-owned approval process set must not declare source owner %q", request.SourceOwner)
	}
	if request.Kind != cp.ComponentKindApprovalProcess {
		return fmt.Errorf("component kind = %q, want %q", request.Kind, cp.ComponentKindApprovalProcess)
	}
	return nil
}

func approvalProcessManagedDesiredSet(keys []string) (map[string]string, map[string]string, error) {
	desired := make(map[string]string, len(keys))
	developerObjects := make(map[string]string, len(keys))
	seen := make(map[string]struct{}, len(keys))
	for _, businessKey := range keys {
		objectAPIName, developerName, err := splitApprovalProcessComponentBusinessKey(businessKey)
		if err != nil {
			return nil, nil, fmt.Errorf("approval process desired set: %w", err)
		}
		identity, err := addApprovalProcessManagedSetIdentity("desired", businessKey, objectAPIName, developerName, seen, developerObjects)
		if err != nil {
			return nil, nil, err
		}
		seen[identity] = struct{}{}
		desired[identity] = businessKey
	}
	return desired, developerObjects, nil
}

func approvalProcessManagedSetRow(packName string, row cp.ComponentRow, seen map[string]struct{}, developerObjects map[string]string) (string, string, bool, error) {
	id, err := approvalProcessManagedProjectionString("ProcessDefinition", row, "Id")
	if err != nil {
		return "", "", false, err
	}
	developerName, err := approvalProcessManagedProjectionString("ProcessDefinition", row, "DeveloperName")
	if err != nil {
		return "", "", false, err
	}
	objectAPIName, err := approvalProcessManagedProjectionString("ProcessDefinition", row, "TableEnumOrId")
	if err != nil {
		return "", "", false, err
	}
	ownerPack, err := approvalProcessManagedProjectionString("ProcessDefinition", row, "OwnerPack")
	if err != nil {
		return "", "", false, err
	}
	if ownerPack != packName {
		return "", "", false, fmt.Errorf("ProcessDefinition %s owner %q escaped owner-scoped query for %q", id, ownerPack, packName)
	}
	if !metadataDeploySafeIdentifier.MatchString(objectAPIName) || !metadataDeploySafeIdentifier.MatchString(developerName) {
		return "", "", false, fmt.Errorf("ProcessDefinition %s has non-canonical business key %q/%q", id, objectAPIName, developerName)
	}
	businessKey := objectAPIName + "/" + developerName
	identity, err := addApprovalProcessManagedSetIdentity("live", businessKey, objectAPIName, developerName, seen, developerObjects)
	if err != nil {
		return "", "", false, err
	}
	seen[identity] = struct{}{}
	state, err := approvalProcessManagedProjectionString("ProcessDefinition", row, "State")
	if err != nil {
		return "", "", false, err
	}
	active, ok := row["IsActive"].(bool)
	if !ok {
		return "", "", false, fmt.Errorf("ProcessDefinition %s has invalid IsActive value %T", id, row["IsActive"])
	}
	wantActive, wantInactive := approvalProcessState(true), approvalProcessState(false)
	if state != wantActive && state != wantInactive {
		return "", "", false, fmt.Errorf("ProcessDefinition %s has invalid State value %q", id, state)
	}
	return identity, developerName, state == wantInactive && !active, nil
}

func approvalProcessManagedDefinitionSetQuery(packName string) cp.ComponentManagedRowQuery {
	return cp.ComponentManagedRowQuery{
		Table:       approvalProcessDefinitionTable,
		Columns:     []string{"Id", "DeveloperName", "TableEnumOrId", "OwnerPack", "State", "IsActive"},
		Equal:       cp.ComponentRow{"OwnerPack": packName},
		OrderBy:     []cp.ComponentManagedOrder{{Column: "Id", Direction: cp.ComponentManagedOrderAscending}},
		MaximumRows: approvalProcessManagedMaximumRows,
	}
}

func marshalApprovalProcessManagedSetProjection(values map[string]string) ([]byte, error) {
	keys := make([]string, 0, len(values))
	for _, businessKey := range values {
		keys = append(keys, businessKey)
	}
	sort.Strings(keys)
	return json.Marshal(approvalProcessManagedSetProjection{BusinessKeys: keys})
}

type approvalProcessManagedLifecycleReader struct {
	store              cp.ComponentManagedReadStore
	lastDefinitionRows int
}

func (r *approvalProcessManagedLifecycleReader) ReadRows(ctx context.Context, query cp.ComponentRowQuery) ([]cp.ComponentRow, error) {
	managed := approvalProcessManagedBoundedQuery(query)
	rows, err := r.store.ReadRows(ctx, managed)
	rows, err = approvalProcessManagedBoundedRows(rows, err, managed)
	if query.Table == approvalProcessDefinitionTable && query.Equal["DeveloperName"] != nil {
		r.lastDefinitionRows = len(rows)
	}
	return rows, err
}

type approvalProcessManagedLifecycleMutationStore struct {
	store cp.ComponentManagedMutationStore
	err   error
}

func (s *approvalProcessManagedLifecycleMutationStore) ReadRows(ctx context.Context, query cp.ComponentRowQuery) ([]cp.ComponentRow, error) {
	if s.err != nil {
		return nil, s.err
	}
	managed := approvalProcessManagedBoundedQuery(query)
	rows, err := s.store.ReadRows(ctx, managed)
	return approvalProcessManagedBoundedRows(rows, err, managed)
}

func (s *approvalProcessManagedLifecycleMutationStore) LockRows(ctx context.Context, query cp.ComponentRowQuery) ([]cp.ComponentRow, error) {
	if s.err != nil {
		return nil, s.err
	}
	managed := approvalProcessManagedBoundedQuery(query)
	rows, err := s.store.LockRows(ctx, managed)
	return approvalProcessManagedBoundedRows(rows, err, managed)
}

// The host store owns the required maximum-plus-one SQL sentinel and must
// return an overflow error instead of a truncated slice. Keep this defensive
// check so a non-conforming store cannot make a capped projection look exact;
// exactly MaximumRows rows remain a valid result.
func approvalProcessManagedBoundedRows(rows []cp.ComponentRow, err error, query cp.ComponentManagedRowQuery) ([]cp.ComponentRow, error) {
	if err != nil {
		return nil, err
	}
	if len(rows) > query.MaximumRows {
		return nil, fmt.Errorf("approval process managed read returned %d rows; maximum is %d", len(rows), query.MaximumRows)
	}
	return rows, nil
}

func (s *approvalProcessManagedLifecycleMutationStore) NewID() string {
	if s.err != nil {
		return ""
	}
	value, err := s.store.NewID()
	if err != nil {
		s.err = err
		return ""
	}
	return value
}

func (s *approvalProcessManagedLifecycleMutationStore) InsertRow(ctx context.Context, insert cp.ComponentInsert) error {
	if s.err != nil {
		return s.err
	}
	logicalKey := cp.ComponentRow{}
	if id, ok := insert.Values["Id"]; ok {
		logicalKey["Id"] = id
	} else {
		return fmt.Errorf("approval process insert into %s is missing Id logical key", insert.Table)
	}
	_, err := s.store.InsertRows(ctx, cp.ComponentManagedInsertBatch{
		Table: insert.Table, Rows: []cp.ComponentManagedInsertRow{{LogicalKey: logicalKey, Values: insert.Values}},
		CurrentTimestampColumns: insert.CurrentTimestampColumns,
	})
	return err
}

func (s *approvalProcessManagedLifecycleMutationStore) UpdateRows(ctx context.Context, update cp.ComponentUpdate) (int64, error) {
	if s.err != nil {
		return 0, s.err
	}
	return s.store.UpdateRows(ctx, cp.ComponentManagedUpdate{
		Table: update.Table, Match: update.Match, Values: update.Values,
		CurrentTimestampColumns: update.CurrentTimestampColumns, MaximumAffectedRows: 1,
	})
}

func (s *approvalProcessManagedLifecycleMutationStore) DeleteRows(ctx context.Context, deletion cp.ComponentDelete) (int64, error) {
	if s.err != nil {
		return 0, s.err
	}
	return s.store.DeleteRows(ctx, cp.ComponentManagedDelete{
		Table: deletion.Table, Match: deletion.Match, MaximumAffectedRows: approvalProcessManagedMaximumRows,
	})
}

func approvalProcessManagedBoundedQuery(query cp.ComponentRowQuery) cp.ComponentManagedRowQuery {
	maximum := approvalProcessManagedMaximumRows
	if query.Table == approvalProcessDefinitionTable {
		if _, byDeveloperName := query.Equal["DeveloperName"]; byDeveloperName {
			maximum = 1
		}
		if _, byID := query.Equal["Id"]; byID {
			maximum = 1
		}
	}
	orderColumn := "Id"
	if query.Table == approvalProcessActionTable && !containsApprovalProcessManagedColumn(query.Columns, orderColumn) {
		orderColumn = query.Columns[0]
	}
	return cp.ComponentManagedRowQuery{
		Table: query.Table, Columns: append([]string(nil), query.Columns...), Equal: query.Equal,
		OrderBy:           []cp.ComponentManagedOrder{{Column: orderColumn, Direction: cp.ComponentManagedOrderAscending}},
		LogicalKeyColumns: []string{orderColumn}, MaximumRows: maximum,
	}
}

func containsApprovalProcessManagedColumn(columns []string, column string) bool {
	for _, candidate := range columns {
		if candidate == column {
			return true
		}
	}
	return false
}
