package dialectsfcrm

import (
	"bytes"
	"context"
	"fmt"
	"reflect"
	"strings"
	"testing"

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

func TestApprovalProcessManagedTargetsUseCaseFoldedPhysicalIdentityAndSharedSetFootprint(t *testing.T) {
	upper, err := ApprovalProcessManagedTargets("Quote_Approval")
	if err != nil {
		t.Fatal(err)
	}
	lower, err := ApprovalProcessManagedTargets("quote_approval")
	if err != nil {
		t.Fatal(err)
	}
	if len(upper) != 2 || len(lower) != 2 || upper[0].TargetDomain != lower[0].TargetDomain || !bytes.Equal(upper[0].PhysicalKeyBytes, lower[0].PhysicalKeyBytes) {
		t.Fatalf("case-equivalent physical targets = %#v / %#v", upper, lower)
	}
	setTarget := approvalProcessManagedExactSetTarget()
	for name, targets := range map[string][]cp.ComponentManagedTarget{"upper": upper, "lower": lower} {
		found := false
		for _, target := range targets {
			found = found || target.TargetDomain == setTarget.TargetDomain && bytes.Equal(target.PhysicalKeyBytes, setTarget.PhysicalKeyBytes)
		}
		if !found {
			t.Fatalf("%s item footprint does not intersect exact-set reconciliation: %#v", name, targets)
		}
	}
}

func TestApprovalProcessManagedLifecycleSetNoChangeUsesExactActiveProjection(t *testing.T) {
	_, registration, err := NewApprovalProcessManagedLifecycleRegistration()
	if err != nil {
		t.Fatal(err)
	}
	set := cp.ComponentManagedSetRequest{PackName: "sales", Kind: cp.ComponentKindApprovalProcess}
	request, err := cp.NewComponentManagedSetLifecycleRequest(set, []string{"Quote/Quote_Approval"})
	if err != nil {
		t.Fatal(err)
	}
	store := &approvalProcessManagedLifecycleTestStore{definitions: []cp.ComponentRow{approvalProcessManagedLifecycleDefinition("definition-1", "Quote", "Quote_Approval", true)}}
	plan, err := registration.Lifecycle().Plan(context.Background(), cp.ComponentManagedScopeSet, request, store)
	if err != nil {
		t.Fatal(err)
	}
	if got := plan.Targets(); len(got) != 2 || got[0].TargetDomain != approvalProcessManagedTargetDomain || string(got[0].PhysicalKeyBytes) != "quote_approval" || got[1].TargetDomain != approvalProcessManagedSetTargetDomain || !reflect.DeepEqual(got[1].PhysicalKeyBytes, []byte{0}) {
		t.Fatalf("set targets = %#v", got)
	}
	result, err := registration.Lifecycle().Mutate(context.Background(), cp.ComponentManagedScopeSet, request, plan, store)
	if err != nil {
		t.Fatal(err)
	}
	if result.Disposition != cp.ComponentManagedMutationNoChange || len(store.updates) != 0 || store.lockCalls != 0 {
		t.Fatalf("no-change result/updates/locks = %#v/%#v/%d", result, store.updates, store.lockCalls)
	}
	projection, err := registration.Lifecycle().Project(context.Background(), cp.ComponentManagedScopeSet, request, store)
	if err != nil {
		t.Fatal(err)
	}
	if string(projection.Expected) != `{"business_keys":["Quote/Quote_Approval"]}` || string(projection.Live) != string(projection.Expected) {
		t.Fatalf("set projection = %s / %s", projection.Expected, projection.Live)
	}
	if err := projection.Validate(); err != nil {
		t.Fatal(err)
	}
}

func TestApprovalProcessManagedLifecycleSetPreservesInactiveDeclarationsAndRepairsOnlyStaleRows(t *testing.T) {
	_, registration, err := NewApprovalProcessManagedLifecycleRegistration()
	if err != nil {
		t.Fatal(err)
	}
	set := cp.ComponentManagedSetRequest{PackName: "sales", Kind: cp.ComponentKindApprovalProcess}
	for name, test := range map[string]struct {
		desired       []string
		state         string
		active        bool
		wantDelta     int
		wantMutations int
	}{
		"declared inactive": {desired: []string{"Quote/Quote_Approval"}, state: approvalProcessState(false)},
		"stale mismatch":    {state: approvalProcessState(false), active: true, wantDelta: 1, wantMutations: 1},
		"already retired":   {state: approvalProcessState(false)},
	} {
		t.Run(name, func(t *testing.T) {
			request, err := cp.NewComponentManagedSetLifecycleRequest(set, test.desired)
			if err != nil {
				t.Fatal(err)
			}
			row := approvalProcessManagedLifecycleDefinition("definition-1", "Quote", "Quote_Approval", test.active)
			row["State"] = test.state
			if name == "declared inactive" {
				row["TableEnumOrId"], row["DeveloperName"] = "quote", "quote_approval"
			}
			store := &approvalProcessManagedLifecycleTestStore{definitions: []cp.ComponentRow{row}}
			plan, err := registration.Lifecycle().Plan(context.Background(), cp.ComponentManagedScopeSet, request, store)
			if err != nil {
				t.Fatal(err)
			}
			if plan.Estimate().DeltaRows != test.wantDelta {
				t.Fatalf("delta estimate = %#v, want %d", plan.Estimate(), test.wantDelta)
			}
			result, err := registration.Lifecycle().Mutate(context.Background(), cp.ComponentManagedScopeSet, request, plan, store)
			if err != nil {
				t.Fatal(err)
			}
			if len(store.updates) != test.wantMutations {
				t.Fatalf("updates = %#v, want %d", store.updates, test.wantMutations)
			}
			if test.wantMutations == 0 && result.Disposition != cp.ComponentManagedMutationNoChange {
				t.Fatalf("no-change result = %#v", result)
			}
			projection, err := registration.Lifecycle().Project(context.Background(), cp.ComponentManagedScopeSet, request, store)
			if err != nil {
				t.Fatal(err)
			}
			if !bytes.Equal(projection.Expected, projection.Live) {
				t.Fatalf("set projection = %s / %s", projection.Expected, projection.Live)
			}
		})
	}
}

func TestApprovalProcessManagedLifecycleSetRetiresStaleRowsThroughBoundedMutation(t *testing.T) {
	_, registration, err := NewApprovalProcessManagedLifecycleRegistration()
	if err != nil {
		t.Fatal(err)
	}
	set := cp.ComponentManagedSetRequest{PackName: "sales", Kind: cp.ComponentKindApprovalProcess}
	request, err := cp.NewComponentManagedSetLifecycleRequest(set, nil)
	if err != nil {
		t.Fatal(err)
	}
	store := &approvalProcessManagedLifecycleTestStore{definitions: []cp.ComponentRow{approvalProcessManagedLifecycleDefinition("definition-1", "Quote", "Quote_Approval", true)}}
	plan, err := registration.Lifecycle().Plan(context.Background(), cp.ComponentManagedScopeSet, request, store)
	if err != nil {
		t.Fatal(err)
	}
	if plan.Estimate().DeltaRows != 1 {
		t.Fatalf("delta estimate = %#v", plan.Estimate())
	}
	result, err := registration.Lifecycle().Mutate(context.Background(), cp.ComponentManagedScopeSet, request, plan, store)
	if err != nil {
		t.Fatal(err)
	}
	if result.Disposition != cp.ComponentManagedMutationDesiredProjectionStaged || store.lockCalls != 1 || len(store.updates) != 1 {
		t.Fatalf("retirement result/locks/updates = %#v/%d/%#v", result, store.lockCalls, store.updates)
	}
	update := store.updates[0]
	if update.MaximumAffectedRows != 1 || update.Table != approvalProcessDefinitionTable || update.Match["Id"] != "definition-1" || update.Values["IsActive"] != false {
		t.Fatalf("bounded retirement update = %#v", update)
	}
	if store.lastLock.MaximumRows != approvalProcessManagedMaximumRows || len(store.lastLock.OrderBy) != 1 || store.lastLock.OrderBy[0].Column != "Id" {
		t.Fatalf("bounded deterministic lock = %#v", store.lastLock)
	}
}

func TestApprovalProcessManagedLifecycleSetFailsClosedOnReadOverflow(t *testing.T) {
	_, registration, err := NewApprovalProcessManagedLifecycleRegistration()
	if err != nil {
		t.Fatal(err)
	}
	request, err := cp.NewComponentManagedSetLifecycleRequest(
		cp.ComponentManagedSetRequest{PackName: "sales", Kind: cp.ComponentKindApprovalProcess}, nil,
	)
	if err != nil {
		t.Fatal(err)
	}
	definitions := make([]cp.ComponentRow, approvalProcessManagedMaximumRows+1)
	for index := range definitions {
		definitions[index] = approvalProcessManagedLifecycleDefinition(fmt.Sprintf("definition-%03d", index), "Quote", fmt.Sprintf("Approval_%03d", index), false)
	}
	store := &approvalProcessManagedLifecycleTestStore{definitions: definitions}
	if _, err := registration.Lifecycle().Plan(context.Background(), cp.ComponentManagedScopeSet, request, store); err == nil || !strings.Contains(err.Error(), "exceeded maximum rows 500") {
		t.Fatalf("overflow plan error = %v", err)
	}
}

func TestApprovalProcessManagedLifecycleRejectsNonconformingStoreOverReturn(t *testing.T) {
	query := cp.ComponentRowQuery{Table: approvalProcessNodeTable, Columns: []string{"Id"}}
	managed := approvalProcessManagedBoundedQuery(query)
	if _, err := approvalProcessManagedBoundedRows(make([]cp.ComponentRow, managed.MaximumRows), nil, managed); err != nil {
		t.Fatalf("exact maximum rows were rejected: %v", err)
	}
	reader := &approvalProcessManagedLifecycleReader{store: approvalProcessManagedLifecycleOverReturnStore{}}
	mutation := &approvalProcessManagedLifecycleMutationStore{store: approvalProcessManagedLifecycleOverReturnStore{}}
	for name, read := range map[string]func() error{
		"read-only":     func() error { _, err := reader.ReadRows(context.Background(), query); return err },
		"mutation read": func() error { _, err := mutation.ReadRows(context.Background(), query); return err },
		"mutation lock": func() error { _, err := mutation.LockRows(context.Background(), query); return err },
	} {
		t.Run(name, func(t *testing.T) {
			if err := read(); err == nil || !strings.Contains(err.Error(), "returned 501 rows; maximum is 500") {
				t.Fatalf("over-return error = %v", err)
			}
		})
	}
}

func TestApprovalProcessManagedLifecycleItemBoundsDesiredChildRowsBeforeStoreRead(t *testing.T) {
	_, registration, err := NewApprovalProcessManagedLifecycleRegistration()
	if err != nil {
		t.Fatal(err)
	}
	for _, child := range []string{"ProcessNode", "ProcessAction"} {
		t.Run(child, func(t *testing.T) {
			request := cp.NewComponentManagedItemLifecycleRequest(approvalProcessManagedCardinalityRequestForTest(t, child, approvalProcessManagedMaximumRows+1))
			store := &approvalProcessManagedLifecycleReadCountingStore{}
			if _, err := registration.Lifecycle().Plan(context.Background(), cp.ComponentManagedScopeItem, request, store); err == nil || !strings.Contains(err.Error(), fmt.Sprintf("501 desired %s rows; maximum is 500", child)) {
				t.Fatalf("desired child overflow error = %v", err)
			}
			if store.readCalls != 0 {
				t.Fatalf("store reads before desired child bound = %d", store.readCalls)
			}
		})
	}
}

func TestApprovalProcessManagedLifecycleItemAcceptsExactDesiredChildRowLimit(t *testing.T) {
	_, registration, err := NewApprovalProcessManagedLifecycleRegistration()
	if err != nil {
		t.Fatal(err)
	}
	for _, child := range []string{"ProcessNode", "ProcessAction"} {
		t.Run(child, func(t *testing.T) {
			request := cp.NewComponentManagedItemLifecycleRequest(approvalProcessManagedCardinalityRequestForTest(t, child, approvalProcessManagedMaximumRows))
			if _, err := registration.Lifecycle().Plan(context.Background(), cp.ComponentManagedScopeItem, request, &approvalProcessManagedLifecycleTestStore{}); err != nil {
				t.Fatalf("exact desired child row limit was rejected: %v", err)
			}
		})
	}
}

func approvalProcessManagedCardinalityRequestForTest(t *testing.T, child string, count int) cp.ComponentManagedRequest {
	t.Helper()
	var raw strings.Builder
	raw.WriteString(`<ApprovalProcess><label>Quote approval</label><active>true</active>`)
	switch child {
	case "ProcessNode":
		for index := 0; index < count; index++ {
			fmt.Fprintf(&raw, "<approvalStep><name>Step_%03d</name></approvalStep>", index)
		}
	case "ProcessAction":
		raw.WriteString(`<initialSubmissionActions>`)
		for index := 0; index < count; index++ {
			fmt.Fprintf(&raw, "<actions><name>Action %03d</name><type>Flow</type><apiName>Flow_%03d</apiName></actions>", index, index)
		}
		raw.WriteString(`</initialSubmissionActions>`)
	default:
		t.Fatalf("unsupported managed cardinality child %q", child)
	}
	raw.WriteString(`</ApprovalProcess>`)
	return approvalProcessManagedRequestFromRawForTest(t, []byte(raw.String()))
}

type approvalProcessManagedLifecycleReadCountingStore struct {
	readCalls int
}

func (s *approvalProcessManagedLifecycleReadCountingStore) ReadRows(context.Context, cp.ComponentManagedRowQuery) ([]cp.ComponentRow, error) {
	s.readCalls++
	return nil, nil
}

func TestApprovalProcessManagedLifecycleItemReactivatesStableNodeIDThroughStructuredStore(t *testing.T) {
	_, registration, err := NewApprovalProcessManagedLifecycleRegistration()
	if err != nil {
		t.Fatal(err)
	}
	request := approvalProcessManagedRequestForTest(t)
	desired, err := decodeApprovalProcessManagedRequest(request)
	if err != nil {
		t.Fatal(err)
	}
	parent := approvalProcessManagedParentValues(desired.Parent)
	parent["Id"] = "definition-1"
	node := approvalProcessManagedNodeValues("definition-1", desired.Nodes[0])
	node["Id"] = "node-review"
	node["IsDeleted"] = true
	store := &approvalProcessManagedLifecycleMemoryStore{
		rows: map[string][]cp.ComponentRow{
			approvalProcessDefinitionTable: {parent},
			approvalProcessNodeTable: {
				node,
				{"Id": "node-old", "Name": "Old", "DeveloperName": "Old", "ProcessDefinitionId": "definition-1", "IsDeleted": false},
			},
			approvalProcessActionTable: {{"Id": "action-old", "Name": "Old", "ProcessDefinitionId": "definition-1", "ProcessNodeId": nil, "IsDeleted": false}},
		},
		ids: []string{"action-flow", "action-clear"},
	}
	lifecycleRequest := cp.NewComponentManagedItemLifecycleRequest(request)
	plan, err := registration.Lifecycle().Plan(context.Background(), cp.ComponentManagedScopeItem, lifecycleRequest, store)
	if err != nil {
		t.Fatal(err)
	}
	result, err := registration.Lifecycle().Mutate(context.Background(), cp.ComponentManagedScopeItem, lifecycleRequest, plan, store)
	if err != nil {
		t.Fatal(err)
	}
	if result.Disposition != cp.ComponentManagedMutationDesiredProjectionStaged {
		t.Fatalf("mutation result = %#v", result)
	}
	if !approvalProcessManagedLifecycleHasUpdate(store.updates, approvalProcessDefinitionTable, "definition-1", nil) ||
		!approvalProcessManagedLifecycleHasUpdate(store.updates, approvalProcessNodeTable, "node-review", cp.ComponentRow{"IsDeleted": false}) ||
		!approvalProcessManagedLifecycleHasUpdate(store.updates, approvalProcessNodeTable, "node-old", cp.ComponentRow{"IsDeleted": true}) {
		t.Fatalf("stable parent/node lifecycle updates = %#v", store.updates)
	}
	if len(store.deletes) != 1 || store.deletes[0].Table != approvalProcessActionTable || store.deletes[0].Match["ProcessDefinitionId"] != "definition-1" {
		t.Fatalf("action replacement delete = %#v", store.deletes)
	}
	if len(store.inserts) != 2 || store.inserts[0].Table != approvalProcessActionTable || store.inserts[1].Table != approvalProcessActionTable {
		t.Fatalf("replacement action inserts = %#v", store.inserts)
	}
	projection, err := registration.Lifecycle().Project(context.Background(), cp.ComponentManagedScopeItem, lifecycleRequest, store)
	if err != nil {
		t.Fatal(err)
	}
	if !bytes.Equal(projection.Expected, projection.Live) {
		t.Fatalf("fresh item projection differs:\n%s\n%s", projection.Expected, projection.Live)
	}
}

func TestApprovalProcessManagedLifecycleItemUsesHostIDsAndRejectsCollisions(t *testing.T) {
	_, registration, err := NewApprovalProcessManagedLifecycleRegistration()
	if err != nil {
		t.Fatal(err)
	}
	request := approvalProcessManagedRequestForTest(t)
	lifecycleRequest := cp.NewComponentManagedItemLifecycleRequest(request)
	created := &approvalProcessManagedLifecycleMemoryStore{
		rows: map[string][]cp.ComponentRow{},
		ids:  []string{"definition-new", "node-new", "action-flow", "action-clear"},
	}
	plan, err := registration.Lifecycle().Plan(context.Background(), cp.ComponentManagedScopeItem, lifecycleRequest, created)
	if err != nil {
		t.Fatal(err)
	}
	if _, err := registration.Lifecycle().Mutate(context.Background(), cp.ComponentManagedScopeItem, lifecycleRequest, plan, created); err != nil {
		t.Fatal(err)
	}
	if len(created.rows[approvalProcessDefinitionTable]) != 1 || created.rows[approvalProcessDefinitionTable][0]["Id"] != "definition-new" ||
		len(created.rows[approvalProcessNodeTable]) != 1 || created.rows[approvalProcessNodeTable][0]["Id"] != "node-new" {
		t.Fatalf("host-allocated parent/node IDs = %#v / %#v", created.rows[approvalProcessDefinitionTable], created.rows[approvalProcessNodeTable])
	}
	projection, err := registration.Lifecycle().Project(context.Background(), cp.ComponentManagedScopeItem, lifecycleRequest, created)
	if err != nil {
		t.Fatal(err)
	}
	if !bytes.Equal(projection.Expected, projection.Live) {
		t.Fatalf("created item projection differs:\n%s\n%s", projection.Expected, projection.Live)
	}

	for name, row := range map[string]cp.ComponentRow{
		"owner":  {"Id": "definition-1", "DeveloperName": "Quote_Approval", "OwnerPack": "other", "TableEnumOrId": "Quote"},
		"object": {"Id": "definition-1", "DeveloperName": "Quote_Approval", "OwnerPack": "sales", "TableEnumOrId": "Order"},
	} {
		t.Run(name, func(t *testing.T) {
			collision := &approvalProcessManagedLifecycleMemoryStore{rows: map[string][]cp.ComponentRow{approvalProcessDefinitionTable: {row}}}
			if _, err := registration.Lifecycle().Plan(context.Background(), cp.ComponentManagedScopeItem, lifecycleRequest, collision); err == nil || len(collision.updates)+len(collision.inserts)+len(collision.deletes) != 0 {
				t.Fatalf("collision error/mutations = %v/%d", err, len(collision.updates)+len(collision.inserts)+len(collision.deletes))
			}
		})
	}
}

func approvalProcessManagedLifecycleHasUpdate(updates []cp.ComponentManagedUpdate, table, id string, values cp.ComponentRow) bool {
	for _, update := range updates {
		if update.Table != table || update.Match["Id"] != id {
			continue
		}
		matches := true
		for column, value := range values {
			matches = matches && reflect.DeepEqual(update.Values[column], value)
		}
		if matches {
			return true
		}
	}
	return false
}

type approvalProcessManagedLifecycleMemoryStore struct {
	rows      map[string][]cp.ComponentRow
	ids       []string
	inserts   []cp.ComponentManagedInsertBatch
	updates   []cp.ComponentManagedUpdate
	deletes   []cp.ComponentManagedDelete
	lockCalls int
}

func (s *approvalProcessManagedLifecycleMemoryStore) ReadRows(_ context.Context, query cp.ComponentManagedRowQuery) ([]cp.ComponentRow, error) {
	return s.read(query)
}

func (s *approvalProcessManagedLifecycleMemoryStore) LockRows(_ context.Context, query cp.ComponentManagedRowQuery) ([]cp.ComponentRow, error) {
	s.lockCalls++
	return s.read(query)
}

func (s *approvalProcessManagedLifecycleMemoryStore) read(query cp.ComponentManagedRowQuery) ([]cp.ComponentRow, error) {
	rows := make([]cp.ComponentRow, 0, len(s.rows[query.Table]))
	for _, row := range s.rows[query.Table] {
		if !approvalProcessManagedLifecycleRowMatches(row, query.Equal) {
			continue
		}
		projection := make(cp.ComponentRow, len(query.Columns))
		for _, column := range query.Columns {
			if value, exists := row[column]; exists {
				projection[column] = value
			}
		}
		rows = append(rows, projection)
		if len(rows) > query.MaximumRows {
			return nil, fmt.Errorf("test managed read exceeded maximum rows %d", query.MaximumRows)
		}
	}
	return rows, nil
}

func (*approvalProcessManagedLifecycleMemoryStore) ActualReceipt() cp.ComponentManagedMutationReceipt {
	return cp.ComponentManagedMutationReceipt{}
}

func (s *approvalProcessManagedLifecycleMemoryStore) NewID() (string, error) {
	if len(s.ids) == 0 {
		return "", fmt.Errorf("test store exhausted host IDs")
	}
	id := s.ids[0]
	s.ids = s.ids[1:]
	return id, nil
}

func (s *approvalProcessManagedLifecycleMemoryStore) InsertRows(_ context.Context, batch cp.ComponentManagedInsertBatch) (int64, error) {
	s.inserts = append(s.inserts, batch)
	for _, insert := range batch.Rows {
		s.rows[batch.Table] = append(s.rows[batch.Table], cloneApprovalProcessManagedRow(insert.Values))
	}
	return int64(len(batch.Rows)), nil
}

func (s *approvalProcessManagedLifecycleMemoryStore) UpdateRows(_ context.Context, update cp.ComponentManagedUpdate) (int64, error) {
	s.updates = append(s.updates, update)
	affected := 0
	for _, row := range s.rows[update.Table] {
		if !approvalProcessManagedLifecycleRowMatches(row, update.Match) {
			continue
		}
		for column, value := range update.Values {
			row[column] = value
		}
		affected++
	}
	if affected > update.MaximumAffectedRows {
		return 0, fmt.Errorf("test update affected %d rows, maximum %d", affected, update.MaximumAffectedRows)
	}
	return int64(affected), nil
}

func (s *approvalProcessManagedLifecycleMemoryStore) DeleteRows(_ context.Context, deletion cp.ComponentManagedDelete) (int64, error) {
	s.deletes = append(s.deletes, deletion)
	kept := s.rows[deletion.Table][:0]
	affected := 0
	for _, row := range s.rows[deletion.Table] {
		if approvalProcessManagedLifecycleRowMatches(row, deletion.Match) {
			affected++
			continue
		}
		kept = append(kept, row)
	}
	if affected > deletion.MaximumAffectedRows {
		return 0, fmt.Errorf("test delete affected %d rows, maximum %d", affected, deletion.MaximumAffectedRows)
	}
	s.rows[deletion.Table] = kept
	return int64(affected), nil
}

func approvalProcessManagedLifecycleRowMatches(row, match cp.ComponentRow) bool {
	for column, want := range match {
		got, exists := row[column]
		if !exists {
			return false
		}
		if column == "DeveloperName" {
			gotText, gotOK := got.(string)
			wantText, wantOK := want.(string)
			if gotOK && wantOK && strings.EqualFold(gotText, wantText) {
				continue
			}
		}
		if !reflect.DeepEqual(got, want) {
			return false
		}
	}
	return true
}

type approvalProcessManagedLifecycleOverReturnStore struct{}

func (approvalProcessManagedLifecycleOverReturnStore) ReadRows(_ context.Context, query cp.ComponentManagedRowQuery) ([]cp.ComponentRow, error) {
	return make([]cp.ComponentRow, query.MaximumRows+1), nil
}

func (approvalProcessManagedLifecycleOverReturnStore) LockRows(_ context.Context, query cp.ComponentManagedRowQuery) ([]cp.ComponentRow, error) {
	return make([]cp.ComponentRow, query.MaximumRows+1), nil
}

func (approvalProcessManagedLifecycleOverReturnStore) NewID() (string, error) { return "unused", nil }

func (approvalProcessManagedLifecycleOverReturnStore) InsertRows(context.Context, cp.ComponentManagedInsertBatch) (int64, error) {
	return 0, nil
}

func (approvalProcessManagedLifecycleOverReturnStore) UpdateRows(context.Context, cp.ComponentManagedUpdate) (int64, error) {
	return 0, nil
}

func (approvalProcessManagedLifecycleOverReturnStore) DeleteRows(context.Context, cp.ComponentManagedDelete) (int64, error) {
	return 0, nil
}

func (approvalProcessManagedLifecycleOverReturnStore) ActualReceipt() cp.ComponentManagedMutationReceipt {
	return cp.ComponentManagedMutationReceipt{}
}

func approvalProcessManagedLifecycleDefinition(id, objectAPIName, developerName string, active bool) cp.ComponentRow {
	return cp.ComponentRow{
		"Id": id, "DeveloperName": developerName, "TableEnumOrId": objectAPIName, "OwnerPack": "sales",
		"State": approvalProcessState(active), "IsActive": active,
	}
}

type approvalProcessManagedLifecycleTestStore struct {
	definitions []cp.ComponentRow
	lastLock    cp.ComponentManagedRowQuery
	lockCalls   int
	updates     []cp.ComponentManagedUpdate
}

func (s *approvalProcessManagedLifecycleTestStore) ReadRows(_ context.Context, query cp.ComponentManagedRowQuery) ([]cp.ComponentRow, error) {
	if query.Table == approvalProcessDefinitionTable {
		if len(s.definitions) > query.MaximumRows {
			return nil, fmt.Errorf("test managed read exceeded maximum rows %d", query.MaximumRows)
		}
		return append([]cp.ComponentRow(nil), s.definitions...), nil
	}
	return nil, nil
}

func (s *approvalProcessManagedLifecycleTestStore) LockRows(_ context.Context, query cp.ComponentManagedRowQuery) ([]cp.ComponentRow, error) {
	s.lockCalls++
	s.lastLock = query
	if query.Table == approvalProcessDefinitionTable {
		if len(s.definitions) > query.MaximumRows {
			return nil, fmt.Errorf("test managed lock exceeded maximum rows %d", query.MaximumRows)
		}
		return append([]cp.ComponentRow(nil), s.definitions...), nil
	}
	return nil, nil
}

func (*approvalProcessManagedLifecycleTestStore) NewID() (string, error) { return "new-id", nil }

func (*approvalProcessManagedLifecycleTestStore) InsertRows(context.Context, cp.ComponentManagedInsertBatch) (int64, error) {
	return 1, nil
}

func (s *approvalProcessManagedLifecycleTestStore) UpdateRows(_ context.Context, update cp.ComponentManagedUpdate) (int64, error) {
	s.updates = append(s.updates, update)
	for _, row := range s.definitions {
		if row["Id"] != update.Match["Id"] {
			continue
		}
		for column, value := range update.Values {
			row[column] = value
		}
	}
	return 1, nil
}

func (*approvalProcessManagedLifecycleTestStore) DeleteRows(context.Context, cp.ComponentManagedDelete) (int64, error) {
	return 0, nil
}

func (*approvalProcessManagedLifecycleTestStore) ActualReceipt() cp.ComponentManagedMutationReceipt {
	return cp.ComponentManagedMutationReceipt{}
}
