package records

import (
	"fmt"
	"go/ast"
	"go/parser"
	"go/token"
	"io/fs"
	"os"
	"path/filepath"
	"regexp"
	"runtime"
	"sort"
	"strings"
	"testing"
	"unicode"
)

type getLockClassification string

const (
	getLockAdmitted          getLockClassification = "admitted"
	getLockNonParentLock     getLockClassification = "non_parent_lock"
	getLockExplicitException getLockClassification = "explicit_exception"
)

type getLockInventoryEntry struct {
	Path           string
	Symbol         string
	Snippet        string
	Classification getLockClassification
	Rationale      string
}

// parentLockGetLockInventory is the checked PR2E inventory for every
// non-test production GetLock occurrence. The snippets intentionally include
// repository/port declarations as well as calls because the source scan uses
// GetLock( as its seed; declarations are classified as primitives/escape-hatch
// policy surfaces, not as parent-row admission boundaries.
var parentLockGetLockInventory = []getLockInventoryEntry{
	{
		Path:           "nexuscrm-automation/approval_submit_runtime.go",
		Symbol:         "func (*ApprovalService).revalidateRuntimeSubmitPlan",
		Snippet:        "lockedRecord, err := s.persistence.GetLock(ctx, req.objectName, req.targetRecordID)",
		Classification: getLockNonParentLock,
		Rationale:      "Runtime Approval locks the exact submit target so definition selection and entry criteria use the current record rather than a pre-lock snapshot; this is the business mutation target, not parent-row admission.",
	},
	{
		Path:           "nexuscrm-automation/approval_runtime_action_fence.go",
		Symbol:         "func (*ApprovalService).FenceRuntimeApprovalAction",
		Snippet:        "instance, err := s.persistence.GetLock(ctx, constants.TableProcessInstance, binding.ProcessInstanceID)",
		Classification: getLockNonParentLock,
		Rationale:      "Runtime Approval serializes all work-item actions on the exact ProcessInstance mutation target before validating the selected work item; this is execution-state fencing, not business parent admission.",
	},
	{
		Path:           "nexuscrm-automation/approval_runtime_action_fence.go",
		Symbol:         "func (*ApprovalService).FenceRuntimeApprovalAction",
		Snippet:        "workItem, err := s.persistence.GetLock(ctx, constants.TableProcessInstanceWorkitem, workItemID)",
		Classification: getLockNonParentLock,
		Rationale:      "After the canonical ProcessInstance lock, Runtime Approval current-reads the exact work-item mutation target to reject duplicate consumption and stale actor authority.",
	},
	{
		Path:           "nexuscrm-automation/executor_actions.go",
		Symbol:         "func (*FlowExecutor).executeRegisterConnectorInboundRemoteAuthorship",
		Snippet:        "committedRec, err := e.persistence.GetLock(ctx, sourceObject, recordId)",
		Classification: getLockNonParentLock,
		Rationale:      "Connector inbound authorship reads the engine-confirmed source mutation row in the ambient atomic flow transaction; it does not lock parent-key admission rows.",
	},
	{
		Path:           "nexuscrm-automation/executor_actions.go",
		Symbol:         "type transferRecordLock struct",
		Snippet:        "record, lockErr := e.persistence.GetLock(ctx, lock.object, lock.id)",
		Classification: getLockNonParentLock,
		Rationale:      "Connector remote-identity transfer locks both endpoint rows in canonical object/id order to validate authority and mirror state; neither is a business parent-row admission lock.",
	},
	{
		Path:           "nexuscrm-automation/executor_connector_inbound_finalize.go",
		Symbol:         "func (*FlowExecutor).resolveConnectorInboundFinalizeRecord",
		Snippet:        "committed, err := e.persistence.GetLock(ctx, sourceObject, recordID)",
		Classification: getLockNonParentLock,
		Rationale:      "Authenticated inbound finalization locks the engine-confirmed mutation row to bind its committed remote-id value to durable evidence; it does not admit a child through a business parent row.",
	},
	{
		Path:           "nexuscrm-automation/executor_connector_field_mapping.go",
		Symbol:         "func (*FlowExecutor).executeApplyConnectorFieldMappings",
		Snippet:        "targetRecord, err := e.persistence.GetLock(txCtx, targetObject, targetID)",
		Classification: getLockNonParentLock,
		Rationale:      "Conflict-policy mapping locks the target mutation row so the authoritative target state and update land atomically; parent rows remain owned by records update prelocks.",
	},
	{
		Path:           "nexuscrm-automation/executor_connector_inbound_recover.go",
		Symbol:         "func (connectorInboundRemoteIdentityRecoverySourceRecordValidator).ValidateConnectorInboundRemoteIdentityRecoverySource",
		Snippet:        "locked, err := v.persistence.GetLock(ctx, objectName, recordID)",
		Classification: getLockNonParentLock,
		Rationale:      "Connector remote-identity recovery locks the exact evidence-authored source row for lifecycle and mirror readback inside the recovery transaction; it is not a business parent-key admission lock.",
	},
	{
		Path:           "nexuscrm-automation/flow_interview_service.go",
		Symbol:         "func (*FlowInterviewService).ClaimPausedInterview",
		Snippet:        "row, err := s.persistence.GetLock(txCtx, constants.TableFlowInterview, interviewId)",
		Classification: getLockNonParentLock,
		Rationale:      "Paused interview scheduler claim locks the _System_FlowInterview row; it is outside business parent-key admission.",
	},
	{
		Path:           "nexuscrm-automation/flow_preview.go",
		Symbol:         "func (*previewPersistence).GetLock",
		Snippet:        "func (p *previewPersistence) GetLock(ctx context.Context, tableName string, id string) (models.SObject, error) {",
		Classification: getLockExplicitException,
		Rationale:      "Preview persistence implements the public port by rejecting lock attempts; no production row lock is acquired through this method.",
	},
	{
		Path:           "nexuscrm-automation/interfaces.go",
		Symbol:         "type PersistenceProvider interface",
		Snippet:        "GetLock(ctx context.Context, tableName string, id string) (models.SObject, error) // SELECT ... FOR UPDATE",
		Classification: getLockExplicitException,
		Rationale:      "Automation exposes the public lock port only for the statically inventoried scheduler/target-mutation callers; new parent-row uses must add a records admission boundary instead.",
	},
	{
		Path:           "nexuscrm-automation/runtime_approval_authority.go",
		Symbol:         "func (*RuntimeApprovalPersistenceRouter).GetLock",
		Snippet:        "func (r *RuntimeApprovalPersistenceRouter) GetLock(ctx context.Context, table, id string) (approvalRuntimeRecord, error) {",
		Classification: getLockNonParentLock,
		Rationale:      "The request-bound Approval adapter preserves the existing persistence port; it does not acquire a lock itself or bypass the records-owned parent-row admission path.",
	},
	{
		Path:           "nexuscrm-automation/runtime_approval_authority.go",
		Symbol:         "func (*RuntimeApprovalPersistenceRouter).GetLock",
		Snippet:        "return r.base.GetLock(ctx, table, id)",
		Classification: getLockNonParentLock,
		Rationale:      "The adapter delegates existing Approval target/runtime-state locks to PersistenceService, whose parent prelocks remain the sole business parent admission owner.",
	},
	{
		Path:           "nexuscrm-kernel/pkg/ports/repositories.go",
		Symbol:         "type RecordRepository interface",
		Snippet:        "GetLock(ctx context.Context, tableName string, id string) (models.SObject, error)",
		Classification: getLockExplicitException,
		Rationale:      "Repository port declaration is the primitive escape hatch; every production caller is separately inventoried and classified.",
	},
	{
		Path:           "nexuscrm-persistence/record_repository.go",
		Symbol:         "func (*RecordRepository).GetLock",
		Snippet:        "func (r *RecordRepository) GetLock(ctx context.Context, tableName string, id string) (models.SObject, error) {",
		Classification: getLockExplicitException,
		Rationale:      "SQL repository primitive emits SELECT ... FOR UPDATE but cannot decide parent-lane admission policy; callers must classify the lock target.",
	},
	{
		Path:           "nexuscrm-records/parent_lock_coordinator.go",
		Symbol:         "func (*PersistenceService).LockParentRows",
		Snippet:        "row, err := ps.repo.GetLock(lockCtx, key.ObjectName, key.RecordID)",
		Classification: getLockAdmitted,
		Rationale:      "This records-layer parent-row lock coordinator now acquires durable parent-lane admission through RunWithParentLaneAdmission before each business parent-row FOR UPDATE.",
	},
	{
		Path:           "nexuscrm-records/permission_set_delete.go",
		Symbol:         "func (*PersistenceService).cascadePermissionSetOwnedReferences",
		Snippet:        "locked, err := ps.repo.GetLock(childCtx, edge.childObjectName, childID)",
		Classification: getLockNonParentLock,
		Rationale:      "Permission definition deletion locks each owned child mutation target after prelockDeleteParentRows and revalidates its acquired parents with ensureDeleteParentRowsAlreadyLocked; business parent admission remains owned by the records coordinator.",
	},
	{
		Path:           "nexuscrm-records/permission_set_delete.go",
		Symbol:         "func (*PersistenceService).removePermissionDefinitionTombstones",
		Snippet:        "locked, err := ps.repo.GetLock(ctx, constants.TableRecycleBin, binID)",
		Classification: getLockNonParentLock,
		Rationale:      "Permission definition purge locks the exact _System_RecycleBin tombstone before verifying its object and record identity and removing it; this is a tombstone mutation target, not a business parent-row admission lock.",
	},
	{
		Path:           "nexuscrm-records/persistence_delete.go",
		Symbol:         "func (*PersistenceService).deleteWithOutcomesAuthorized",
		Snippet:        "locked, err := ps.repo.GetLock(txCtx, objectName, id)",
		Classification: getLockNonParentLock,
		Rationale:      "Delete locks the target row being deleted after delete parent prelocks have run; the parent-key admission boundary is prelockDeleteParentRows/LockParentRows, not this target lock.",
	},
	{
		Path:           "nexuscrm-records/persistence_raw_access.go",
		Symbol:         "func (*PersistenceService).GetLock",
		Snippet:        "func (ps *PersistenceService) GetLock(ctx context.Context, tableName string, id string) (models.SObject, error) {",
		Classification: getLockExplicitException,
		Rationale:      "Public PersistenceService escape hatch is retained for inventoried non-parent callers only; parent-row business locks must use records admission paths.",
	},
	{
		Path:           "nexuscrm-records/persistence_raw_access.go",
		Symbol:         "func (*PersistenceService).GetLock",
		Snippet:        "return ps.repo.GetLock(ctx, tableName, id)",
		Classification: getLockExplicitException,
		Rationale:      "Delegate for the public escape hatch; the caller inventory is the policy gate preventing unconstrained business parent-row use.",
	},
	{
		Path:           "nexuscrm-records/persistence_raw_access.go",
		Symbol:         "func (*PersistenceService).PhysicalDeleteRaw",
		Snippet:        "before, err = ps.repo.GetLock(txCtx, objectName, id)",
		Classification: getLockNonParentLock,
		Rationale:      "Permission-state protected raw delete locks the exact mutation target before physical removal and revision advance; live-parent admission remains a separate earlier gate.",
	},
	{
		Path:           "nexuscrm-records/persistence_recyclebin_restore.go",
		Symbol:         "func (*PersistenceService).lockRecycleBinTombstonesForMutation",
		Snippet:        "lockedRow, err := ps.repo.GetLock(ctx, constants.TableRecycleBin, currentTombstone.id)",
		Classification: getLockNonParentLock,
		Rationale:      "Recycle-bin mutation locks the tombstone authority row in _System_RecycleBin, not a business parent row.",
	},
	{
		Path:           "nexuscrm-records/persistence_recyclebin_restore_cascade.go",
		Symbol:         "func (*PersistenceService).validateRecycleBinRestoreCascadeRecords",
		Snippet:        "lockedRecord, err := ps.repo.GetLock(ctx, tombstone.objectName, tombstone.recordID)",
		Classification: getLockNonParentLock,
		Rationale:      "Cascade restore validation locks deleted descendant target rows to verify lineage state; these are restored records, not parent-key admission locks.",
	},
	{
		Path:           "nexuscrm-records/persistence_recyclebin_restore_cascade.go",
		Symbol:         "func (*PersistenceService).validateRecycleBinRestoreCascadeCompleteness",
		Snippet:        "lockedChild, err := ps.repo.GetLock(ctx, edge.childObjectName, childID)",
		Classification: getLockNonParentLock,
		Rationale:      "Cascade completeness checks lock candidate deleted child rows to detect non-lineage conflicts; parent admission is not bypassed by this validation lock.",
	},
	{
		Path:           "nexuscrm-records/persistence_update_helpers.go",
		Symbol:         "func (*PersistenceService).loadAndValidateAccessForOperation",
		Snippet:        "oldRecord, err := ps.repo.GetLock(txCtx, objectName, id)",
		Classification: getLockNonParentLock,
		Rationale:      "Update locks the target mutation row for access validation and write isolation; parent-row prelocks are handled before the target update path.",
	},
	{
		Path:           "nexuscrm-records/persistence_update_helpers.go",
		Symbol:         "func (*PersistenceService).loadAndValidateAccessForUpdateAuthorization",
		Snippet:        "oldRecord, err := ps.repo.GetLock(txCtx, objectName, id)",
		Classification: getLockNonParentLock,
		Rationale:      "Authorized update locks the target mutation row while preserving permission checks; it is not a parent-key admission row.",
	},
}

func TestParentLockGetLockInventoryIsComplete(t *testing.T) {
	repoRoot := parentLockInventoryRepoRoot(t)
	actual := scanProductionGetLockSites(t, repoRoot)

	expectedByKey := make(map[string]getLockInventoryEntry, len(parentLockGetLockInventory))
	for _, entry := range parentLockGetLockInventory {
		if entry.Path == "" || entry.Symbol == "" || entry.Snippet == "" {
			t.Fatalf("inventory entry must include path, symbol, and snippet: %+v", entry)
		}
		if entry.Rationale == "" {
			t.Fatalf("inventory entry must include rationale: %+v", entry)
		}
		switch entry.Classification {
		case getLockAdmitted, getLockNonParentLock, getLockExplicitException:
		default:
			t.Fatalf("invalid inventory classification %q for %s", entry.Classification, entry.key())
		}
		key := entry.key()
		if previous, ok := expectedByKey[key]; ok {
			t.Fatalf("duplicate inventory entry:\n%s\nprevious: %+v\ncurrent: %+v", key, previous, entry)
		}
		expectedByKey[key] = entry
	}

	actualByKey := make(map[string]getLockSite, len(actual))
	for _, site := range actual {
		if previous, ok := actualByKey[site.key()]; ok {
			t.Fatalf("ambiguous production GetLock site key:\n%s\nprevious: %+v\ncurrent: %+v", site.key(), previous, site)
		}
		actualByKey[site.key()] = site
	}

	var missing []string
	for _, site := range actual {
		if _, ok := expectedByKey[site.key()]; !ok {
			missing = append(missing, site.inventoryTemplate())
		}
	}

	var stale []string
	for _, entry := range parentLockGetLockInventory {
		if _, ok := actualByKey[entry.key()]; !ok {
			stale = append(stale, entry.key())
		}
	}

	if len(missing) > 0 || len(stale) > 0 {
		sort.Strings(missing)
		sort.Strings(stale)
		t.Fatalf("GetLock inventory mismatch\n\nUnclassified production GetLock occurrences:\n%s\n\nStale inventory entries:\n%s", strings.Join(missing, "\n"), strings.Join(stale, "\n"))
	}
}

func TestParentLockGetLockInventoryPublicEscapeHatchPolicy(t *testing.T) {
	for _, entry := range parentLockGetLockInventory {
		if !strings.HasPrefix(entry.Path, "nexuscrm-automation/") {
			continue
		}
		if !strings.Contains(entry.Snippet, ".GetLock(") {
			continue
		}
		if entry.Classification != getLockNonParentLock {
			t.Fatalf("automation PersistenceProvider.GetLock caller must remain non_parent_lock, got %s for %s", entry.Classification, entry.key())
		}
	}
}

type pessimisticOwnerClassification string

const (
	pessimisticRetryOwner      pessimisticOwnerClassification = "retry_owner"
	pessimisticFailClosedOwner pessimisticOwnerClassification = "fail_closed_owner"
)

type pessimisticOwnerInventoryEntry struct {
	Path           string
	Boundary       string
	Count          int
	Classification pessimisticOwnerClassification
	Rationale      string
}

// parentLockPessimisticOwnerInventory classifies every direct production
// consumer of the pessimistic transaction boundaries. RunIn owners may replay
// the complete callback to converge newly discovered parent prelocks. The
// outcome-returning RunIn variant has the same replay ownership plus a typed
// terminal commit/rollback result. Raw With owners fail closed on a retryable
// prelock abort because their callback side effects have not declared replay
// safety.
var parentLockPessimisticOwnerInventory = []pessimisticOwnerInventoryEntry{
	{Path: "backend/internal/application/services/metadata_deploy_contract.go", Boundary: "RunInPessimisticTransaction", Count: 2, Classification: pessimisticRetryOwner, Rationale: "Metadata deployment delegates upsert and required-match delete mutations to the records-owned replay boundary."},
	{Path: "backend/internal/runtimeapi/approval_server.go", Boundary: "RunInPessimisticTransactionWithOutcome", Count: 1, Classification: pessimisticRetryOwner, Rationale: "Runtime Approval serving replays one immutable request through Catalog resolution, durable acceptance, existing Approval execution, permission fencing, and terminal receipt in the same records-owned transaction; typed terminal outcome proof is required before a rolled-back Validation Rule failure can be presented."},
	{Path: "backend/internal/runtimeapi/flow_server.go", Boundary: "RunInPessimisticTransactionWithOutcome", Count: 1, Classification: pessimisticRetryOwner, Rationale: "Runtime Flow serving owns one idempotency-fenced acceptance, execution, and completion callback whose full transaction may replay for parent-prelock convergence; typed terminal outcome proof is required before approved Flow errors can be presented."},
	{Path: "backend/internal/runtimeapi/mailbox_authorization_runtime.go", Boundary: "RunInPessimisticTransaction", Count: 1, Classification: pessimisticRetryOwner, Rationale: "Runtime mailbox callback consumption replays one immutable handle through its row lock, current-session and exact Permission State fences, and database-clock-bounded one-time transition."},
	{Path: "backend/internal/runtimeapi/outbound_intent_server.go", Boundary: "RunInPessimisticTransaction", Count: 2, Classification: pessimisticRetryOwner, Rationale: "Provider-neutral outbound acknowledgement and result commit replay the same immutable, idempotency-fenced request through the records-owned convergence boundary."},
	{Path: "backend/internal/runtimeworker/assemble.go", Boundary: "RunInPessimisticTransactionWithOutcome", Count: 1, Classification: pessimisticRetryOwner, Rationale: "Runtime Worker composition adapts the scheduled reservation owner to the records-owned parent-prelock convergence boundary while preserving the shared transaction extractor."},
	{Path: "nexuscrm-automation/approval_action_process.go", Boundary: "RunInPessimisticTransaction", Count: 1, Classification: pessimisticRetryOwner, Rationale: "Approval action processing owns an atomic records transaction whose callback is replayable."},
	{Path: "nexuscrm-automation/approval_reassign.go", Boundary: "RunInPessimisticTransaction", Count: 1, Classification: pessimisticRetryOwner, Rationale: "Approval reassignment uses the records convergence contract."},
	{Path: "nexuscrm-automation/approval_recall.go", Boundary: "RunInPessimisticTransaction", Count: 1, Classification: pessimisticRetryOwner, Rationale: "Approval recall uses the records convergence contract."},
	{Path: "nexuscrm-automation/approval_submit_runtime.go", Boundary: "RunInPessimisticTransaction", Count: 1, Classification: pessimisticRetryOwner, Rationale: "Approval submission uses the records convergence contract."},
	{Path: "nexuscrm-automation/runtime_approval_authority.go", Boundary: "RunInPessimisticTransaction", Count: 1, Classification: pessimisticRetryOwner, Rationale: "The Runtime Approval adapter delegates the existing Approval engine's transaction boundary to the same records-owned replay contract without introducing a second transaction owner."},
	{Path: "nexuscrm-automation/connector_inbound_remote_identity_recovery.go", Boundary: "RunInPessimisticTransaction", Count: 1, Classification: pessimisticRetryOwner, Rationale: "Strict single-item connector remote-identity recovery replays one immutable request through its full hint, lock, preflight, and finalization transaction."},
	{Path: "nexuscrm-automation/connector_inbound_remote_identity_recovery.go", Boundary: "RunInPessimisticTransactionWithOutcome", Count: 1, Classification: pessimisticRetryOwner, Rationale: "Bounded connector recovery owns replay and requires the typed terminal outcome before it can claim rollback or commit."},
	{Path: "nexuscrm-automation/executor_connector_field_mapping.go", Boundary: "RunInPessimisticTransaction", Count: 1, Classification: pessimisticRetryOwner, Rationale: "Connector field mapping declares its transactional callback replayable through the records boundary."},
	{Path: "nexuscrm-automation/flow_executor.go", Boundary: "RunInPessimisticTransaction", Count: 1, Classification: pessimisticRetryOwner, Rationale: "Atomic Flow execution is the primary replay owner for nested record writes."},
	{Path: "nexuscrm-automation/flow_executor.go", Boundary: "RunInPessimisticTransactionWithOutcome", Count: 1, Classification: pessimisticRetryOwner, Rationale: "Atomic Flow execution uses the typed terminal outcome when the persistence implementation supports it; this is the same replay owner as the compatibility fallback."},
	{Path: "nexuscrm-automation/fenced_scheduled_run_reservation.go", Boundary: "RunInPessimisticTransactionWithOutcome", Count: 1, Classification: pessimisticRetryOwner, Rationale: "The scheduled work-family owner replays one immutable accepted input while locking its exact attempt, then commits the bounded Flow effect and COMPLETED receipt together; ambiguous terminal outcomes remain non-terminal."},
	{Path: "nexuscrm-persistence/runtime_platform_command_execution.go", Boundary: "RunInPessimisticTransactionWithOutcome", Count: 1, Classification: pessimisticRetryOwner, Rationale: "The Platform-command owner replays one immutable accepted command while locking its exact lease and work-family epoch, then commits the bounded Runtime effect and COMPLETED result together; external effects are forbidden."},
	{Path: "nexuscrm-automation/flow_interview_service.go", Boundary: "RunInPessimisticTransaction", Count: 1, Classification: pessimisticRetryOwner, Rationale: "Flow interview claim uses the records-owned retry boundary."},
	{Path: "backend/distributions/crm/send_record_email_runtime.go", Boundary: "RunInPessimisticTransaction", Count: 1, Classification: pessimisticRetryOwner, Rationale: "Accepted connected-email activity materialization owns an idempotent two-record transaction with deterministic IDs and immutable replay validation."},
	{Path: "nexuscrm-app-packs/dialects/dialect-sf-crm/standard_actions_legacy.go", Boundary: "RunInPessimisticTransaction", Count: 2, Classification: pessimisticRetryOwner, Rationale: "CRM legacy create-record and change-owner standard actions delegate their record writes through RecordMutationCoordinator, so parent-prelock convergence is owned by the records replay boundary."},
	{Path: "nexuscrm-app-packs-frontend/dialects/dialect-sf-crm/standard_actions_legacy.go", Boundary: "RunInPessimisticTransaction", Count: 2, Classification: pessimisticRetryOwner, Rationale: "The frontend app-packs checkout supplies the same CRM legacy standard-action source during CI; its record mutations also delegate replay to RecordMutationCoordinator."},
	{Path: "nexuscrm-metadata/listview_authoring_contract.go", Boundary: "RunInPessimisticTransaction", Count: 1, Classification: pessimisticRetryOwner, Rationale: "List-view authoring delegates transactional replay to its records lifecycle port."},
	{Path: "nexuscrm-records/contract_adapter.go", Boundary: "RunInPessimisticTransaction", Count: 2, Classification: pessimisticRetryOwner, Rationale: "The records contract adapter exposes and consumes the canonical replay boundary."},
	{Path: "nexuscrm-records/group_membership_closure.go", Boundary: "RunInPessimisticTransactionWithOutcome", Count: 3, Classification: pessimisticRetryOwner, Rationale: "Group membership closure rebuild owns replayable Records transactions and preserves typed terminal outcomes for durable effect/checkpoint callers."},
	{Path: "nexuscrm-records/persistence_insert.go", Boundary: "RunInPessimisticTransaction", Count: 1, Classification: pessimisticRetryOwner, Rationale: "Insert preview finalization runs under the records-owned convergence boundary."},
	{Path: "nexuscrm-analytics/rollup_service.go", Boundary: "RunInPessimisticTransaction", Count: 1, Classification: pessimisticRetryOwner, Rationale: "Rollup maintenance replays one detached parent recalculation from forced parent prelocks through the current-read aggregate and derived save."},
	{Path: "backend/internal/runtimeassembly/application_navigation.go", Boundary: "WithPessimisticTransaction", Count: 1, Classification: pessimisticFailClosedOwner, Rationale: "Application navigation locks the application before authorization, then stores the document, revision, audit and receipt atomically; exact request replay is explicit and the transaction callback has no implicit replay contract."},
	{Path: "nexuscrm-automation/action_service.go", Boundary: "WithPessimisticTransaction", Count: 1, Classification: pessimisticFailClosedOwner, Rationale: "Composite action orchestration has not declared its mutable callback state replay safe."},
	{Path: "nexuscrm-automation/approval_action_replay.go", Boundary: "WithPessimisticTransaction", Count: 1, Classification: pessimisticFailClosedOwner, Rationale: "Approval action replay must not recursively replay its transaction callback."},
	{Path: "nexuscrm-events/outbox_service.go", Boundary: "WithPessimisticTransaction", Count: 2, Classification: pessimisticFailClosedOwner, Rationale: "Outbox ownership-fenced claiming and already-claimed dispatch state are fail-closed and must not be implicitly replayed."},
	{Path: "nexuscrm-metadata/metadata_flows.go", Boundary: "WithPessimisticTransaction", Count: 1, Classification: pessimisticFailClosedOwner, Rationale: "Metadata flow persistence has no parent-prelock callback replay contract."},
	{Path: "nexuscrm-metadata/metadata_layouts.go", Boundary: "WithPessimisticTransaction", Count: 1, Classification: pessimisticFailClosedOwner, Rationale: "Layout authoring owns one aggregate-version-fenced metadata transaction and defers cache invalidation until commit; its caller callback has no implicit replay contract."},
	{Path: "nexuscrm-permissions/admin_service.go", Boundary: "WithPessimisticTransaction", Count: 1, Classification: pessimisticFailClosedOwner, Rationale: "Permission administration owns the PermissionSet lock, semantic grant mutations, and Permission State advance in one non-replayed transaction."},
	{Path: "nexuscrm-permissions/permission_authoring_read.go", Boundary: "WithCurrentReadPermissionAuthoringTransaction", Count: 1, Classification: pessimisticFailClosedOwner, Rationale: "Permission authoring snapshot reads lock one canonical PermissionSet root before assembling its complete stored image and compare token; the read callback has no replay contract and fails closed on transaction contention."},
	{Path: "nexuscrm-permissions/permission_authoring_service.go", Boundary: "WithCurrentReadPermissionAuthoringTransaction", Count: 3, Classification: pessimisticFailClosedOwner, Rationale: "Atomic Permission Set authoring separates durable acceptance, immutable target binding, and aggregate execution into fail-closed READ COMMITTED transactions; command identity makes caller retry exact without implicitly replaying transaction callbacks."},
	{Path: "nexuscrm-persistence/file_upload_operation_repository.go", Boundary: "WithPessimisticTransaction", Count: 1, Classification: pessimisticFailClosedOwner, Rationale: "File upload finalization invokes caller-owned metadata mutation before its fenced completion transition and has no callback replay contract."},
	{Path: "nexuscrm-persistence/metadata_repository.go", Boundary: "WithPessimisticTransaction", Count: 1, Classification: pessimisticFailClosedOwner, Rationale: "Grouped direct metadata writers share one transaction whose callback has no parent-prelock replay contract."},
	{Path: "nexuscrm-persistence/permission_state_direct_projection.go", Boundary: "WithPessimisticTransaction", Count: 1, Classification: pessimisticFailClosedOwner, Rationale: "Grouped direct permission writers share one target-lock and Permission State transaction whose callback has no replay contract."},
	{Path: "nexuscrm-persistence/permission_state_direct_writer.go", Boundary: "WithPessimisticTransaction", Count: 1, Classification: pessimisticFailClosedOwner, Rationale: "The direct-writer coordinator owns semantic before/after projection and the Permission State advance in one fail-closed transaction."},
	{Path: "nexuscrm-persistence/schema_ddl_ops.go", Boundary: "WithPessimisticTransaction", Count: 2, Classification: pessimisticFailClosedOwner, Rationale: "Strict schema creation performs physical DDL before its metadata transaction and registers rollback compensation; table removal owns live-cascade preflight and metadata unregister in a separate transaction. Neither callback is safe to replay implicitly."},
	{Path: "nexuscrm-persistence/system_repository.go", Boundary: "WithPessimisticTransaction", Count: 1, Classification: pessimisticFailClosedOwner, Rationale: "Connected App credential issuance consumes one-time authorization state and generates credentials, so lock contention must fail closed rather than replay the callback."},
	{Path: "nexuscrm-records/rollup_delta_queue.go", Boundary: "WithPessimisticTransaction", Count: 2, Classification: pessimisticFailClosedOwner, Rationale: "Runtime rollup definition failures lock one exact durable row and the deployment fence; retrying either callback could advance the poison attempt state twice."},
}

func TestParentLockPessimisticTransactionOwnerInventoryIsComplete(t *testing.T) {
	repoRoot := parentLockInventoryRepoRoot(t)
	actual := scanProductionPessimisticOwnerSites(t, repoRoot)
	expected := make(map[string]pessimisticOwnerInventoryEntry, len(parentLockPessimisticOwnerInventory))
	for _, entry := range parentLockPessimisticOwnerInventory {
		if entry.Path == "" || entry.Boundary == "" || entry.Count < 1 || entry.Rationale == "" {
			t.Fatalf("incomplete pessimistic owner inventory entry: %+v", entry)
		}
		switch entry.Classification {
		case pessimisticRetryOwner:
			if entry.Boundary != "RunInPessimisticTransaction" && entry.Boundary != "RunInPessimisticTransactionWithOutcome" {
				t.Fatalf("retry owner %s must use RunInPessimisticTransaction", entry.Path)
			}
		case pessimisticFailClosedOwner:
			if entry.Boundary != "WithPessimisticTransaction" && entry.Boundary != "WithCurrentReadPermissionAuthoringTransaction" {
				t.Fatalf("fail-closed owner %s must use WithPessimisticTransaction", entry.Path)
			}
		default:
			t.Fatalf("invalid pessimistic owner classification %q for %s", entry.Classification, entry.Path)
		}
		key := entry.Path + "\x00" + entry.Boundary
		if _, exists := expected[key]; exists {
			t.Fatalf("duplicate pessimistic owner inventory entry %q", key)
		}
		if !pessimisticOwnerInventoryEntryActive(repoRoot, entry) {
			continue
		}
		expected[key] = entry
	}

	var mismatches []string
	for key, count := range actual {
		entry, ok := expected[key]
		if !ok {
			mismatches = append(mismatches, fmt.Sprintf("unclassified %s count=%d", key, count))
			continue
		}
		if entry.Count != count {
			mismatches = append(mismatches, fmt.Sprintf("count changed %s got=%d want=%d", key, count, entry.Count))
		}
		delete(expected, key)
	}
	for key := range expected {
		mismatches = append(mismatches, "stale "+key)
	}
	if len(mismatches) > 0 {
		sort.Strings(mismatches)
		t.Fatalf("pessimistic transaction owner inventory mismatch:\n%s", strings.Join(mismatches, "\n"))
	}
}

func pessimisticOwnerInventoryEntryActive(repoRoot string, entry pessimisticOwnerInventoryEntry) bool {
	root, _, _ := strings.Cut(entry.Path, "/")
	switch root {
	case "nexuscrm-app-packs", "nexuscrm-app-packs-frontend":
		_, err := os.Stat(filepath.Join(repoRoot, root))
		return err == nil
	default:
		return true
	}
}

func scanProductionPessimisticOwnerSites(t *testing.T, repoRoot string) map[string]int {
	t.Helper()
	sites := map[string]int{}
	err := filepath.WalkDir(repoRoot, func(path string, d fs.DirEntry, walkErr error) error {
		if walkErr != nil {
			return walkErr
		}
		if d.IsDir() {
			switch d.Name() {
			case ".git", ".runtime", "node_modules":
				return filepath.SkipDir
			}
			return nil
		}
		if !strings.HasSuffix(path, ".go") || strings.HasSuffix(path, "_test.go") {
			return nil
		}
		rel, err := filepath.Rel(repoRoot, path)
		if err != nil {
			return err
		}
		rel = filepath.ToSlash(rel)
		if rel == "nexuscrm-persistence/transaction_manager.go" || rel == "nexuscrm-records/persistence_transactions.go" {
			return nil
		}
		parsed, err := parser.ParseFile(token.NewFileSet(), path, nil, 0)
		if err != nil {
			return err
		}
		ast.Inspect(parsed, func(node ast.Node) bool {
			call, ok := node.(*ast.CallExpr)
			if !ok {
				return true
			}
			selector, ok := call.Fun.(*ast.SelectorExpr)
			if !ok {
				return true
			}
			switch selector.Sel.Name {
			case "RunInPessimisticTransaction", "RunInPessimisticTransactionWithOutcome", "WithPessimisticTransaction", "WithCurrentReadPermissionAuthoringTransaction":
				sites[rel+"\x00"+selector.Sel.Name]++
			}
			return true
		})
		return nil
	})
	if err != nil {
		t.Fatalf("scan production pessimistic transaction owners: %v", err)
	}
	return sites
}

type getLockSite struct {
	Path    string
	Symbol  string
	Snippet string
}

func (e getLockInventoryEntry) key() string {
	return strings.Join([]string{e.Path, e.Symbol, e.Snippet}, "\x00")
}

func (s getLockSite) key() string {
	return strings.Join([]string{s.Path, s.Symbol, s.Snippet}, "\x00")
}

func (s getLockSite) inventoryTemplate() string {
	return fmt.Sprintf("{Path: %q, Symbol: %q, Snippet: %q, Classification: <classify>, Rationale: <why>},", s.Path, s.Symbol, s.Snippet)
}

func parentLockInventoryRepoRoot(t *testing.T) string {
	t.Helper()
	_, file, _, ok := runtime.Caller(0)
	if !ok {
		t.Fatal("failed to locate parent lock inventory test file")
	}
	return filepath.Dir(filepath.Dir(file))
}

func scanProductionGetLockSites(t *testing.T, repoRoot string) []getLockSite {
	t.Helper()
	var sites []getLockSite
	err := filepath.WalkDir(repoRoot, func(path string, d fs.DirEntry, walkErr error) error {
		if walkErr != nil {
			return walkErr
		}
		if d.IsDir() {
			switch d.Name() {
			case ".git", ".runtime", "node_modules":
				return filepath.SkipDir
			}
			return nil
		}
		if !strings.HasSuffix(path, ".go") || strings.HasSuffix(path, "_test.go") {
			return nil
		}
		rel, err := filepath.Rel(repoRoot, path)
		if err != nil {
			return err
		}
		rel = filepath.ToSlash(rel)
		contents, err := os.ReadFile(path)
		if err != nil {
			return err
		}

		symbol := "package"
		for _, line := range strings.Split(string(contents), "\n") {
			trimmed := strings.TrimSpace(line)
			if parsed := typeSymbol(trimmed); parsed != "" {
				symbol = parsed
			}
			if parsed := funcSymbol(trimmed); parsed != "" {
				symbol = parsed
			}
			if strings.Contains(line, "GetLock(") {
				sites = append(sites, getLockSite{Path: rel, Symbol: symbol, Snippet: trimmed})
			}
		}
		return nil
	})
	if err != nil {
		t.Fatalf("scan production GetLock sites: %v", err)
	}
	sort.Slice(sites, func(i, j int) bool { return sites[i].key() < sites[j].key() })
	return sites
}

var typeSymbolPattern = regexp.MustCompile(`^type\s+([A-Za-z_][A-Za-z0-9_]*)\s+(interface|struct)\b`)

func typeSymbol(line string) string {
	matches := typeSymbolPattern.FindStringSubmatch(line)
	if len(matches) != 3 {
		return ""
	}
	return "type " + matches[1] + " " + matches[2]
}

func funcSymbol(line string) string {
	if !strings.HasPrefix(line, "func ") {
		return ""
	}
	rest := strings.TrimSpace(strings.TrimPrefix(line, "func "))
	if strings.HasPrefix(rest, "(") {
		end := strings.Index(rest, ")")
		if end < 0 {
			return ""
		}
		receiver := strings.TrimSpace(rest[1:end])
		fields := strings.Fields(receiver)
		if len(fields) == 0 {
			return ""
		}
		receiverType := fields[len(fields)-1]
		rest = strings.TrimSpace(rest[end+1:])
		name := leadingIdentifier(rest)
		if name == "" {
			return ""
		}
		return fmt.Sprintf("func (%s).%s", receiverType, name)
	}
	name := leadingIdentifier(rest)
	if name == "" {
		return ""
	}
	return "func " + name
}

func leadingIdentifier(s string) string {
	for i, r := range s {
		if i == 0 {
			if r != '_' && !unicode.IsLetter(r) {
				return ""
			}
			continue
		}
		if r != '_' && !unicode.IsLetter(r) && !unicode.IsDigit(r) {
			return s[:i]
		}
	}
	return s
}
