package runtimeapi

import (
	"context"
	"database/sql"
	"sync/atomic"
	"time"

	"github.com/nexuscrm/kernel/pkg/perftiming"
	"github.com/nexuscrm/kernel/pkg/telemetry"
	catalogv1 "github.com/pingcap-inc/nexuscrm-contract/wire-go/gen/catalog/v1"
	runtimev1 "github.com/pingcap-inc/nexuscrm-contract/wire-go/gen/runtime/v1"
	"github.com/prometheus/client_golang/prometheus"
	"google.golang.org/grpc"
)

const (
	recordsOperationSelectedRoot            = "read_selected_root"
	recordsOperationAccess                  = "evaluate_record_access"
	recordsOperationQuery                   = "query_records"
	recordsOperationQueryHistory            = "query_history"
	recordsOperationRelatedCount            = "count_related_records"
	recordsOperationCreateDefaultPreview    = "preview_create_defaults"
	recordsOperationRecycleBin              = "list_recycle_bin"
	recordsOperationRecycleBinRestore       = "restore_recycle_bin"
	recordsOperationRecycleBinPurge         = "purge_recycle_bin"
	recordsOperationRecordViewRead          = "read_record_view"
	recordsOperationAttachmentSourceResolve = "resolve_attachment_sources"
	recordsOperationMutate                  = "mutate_records"
	recordsOperationMutationStatus          = "get_mutation_status"
	runtimeOperationIdentity                = "identity_exchange"
	runtimeOperationAppMetadataV3           = "read_app_metadata_v3"
	runtimeOperationEffectivePermissions    = "read_effective_permissions"
	runtimeOperationSearchRecords           = "search_records"
	runtimeOperationSearchScopeOrder        = "read_search_scope_order"
	runtimeOperationExecuteFlow             = "execute_flow"
	runtimeOperationPreviewCatalogAction    = "preview_catalog_action"
	runtimeOperationExecuteApproval         = "execute_approval"
	runtimeOperationReadApproval            = "read_approval"
	runtimeOperationPermissionRead          = "read_permission_authoring_snapshot"
	runtimeOperationPermissionSave          = "save_permission_authoring"
	runtimeOperationPermissionStatus        = "get_permission_authoring_status"
	runtimeRPCPhaseTotal                    = "total"
	runtimeRPCPhasePostSecurityAdmission    = "post_security_admission"
	runtimeRPCPhaseWorkloadBinding          = "workload_binding"
	runtimeRPCPhaseDelegationVerification   = "delegation_verification"
	runtimeRPCPhaseSecurityState            = "security_state"
	runtimeRPCPhaseSession                  = "session"
)

type runtimeRPCPhaseProfile struct {
	observations  atomic.Uint64
	nanos         atomic.Uint64
	sqlStatements atomic.Uint64
	sqlNanos      atomic.Uint64
}

type runtimeRPCProfile struct {
	operation string
	phases    map[string]*runtimeRPCPhaseProfile
}

func newRuntimeRPCProfile(operation string) *runtimeRPCProfile {
	return &runtimeRPCProfile{operation: operation, phases: map[string]*runtimeRPCPhaseProfile{
		runtimeRPCPhaseTotal:                  {},
		runtimeRPCPhasePostSecurityAdmission:  {},
		runtimeRPCPhaseWorkloadBinding:        {},
		runtimeRPCPhaseDelegationVerification: {},
		runtimeRPCPhaseSecurityState:          {},
		runtimeRPCPhaseSession:                {},
	}}
}

// recordsMetrics observes the Runtime-owned repository boundary and the
// Runtime-owned database pool. Labels are a closed operation vocabulary; no
// org, App, object, field, record, SQL, or user value is exported.
type recordsMetrics struct {
	pool *sql.DB

	selectedRootRepositoryCalls          atomic.Uint64
	selectedRootSQLStatements            atomic.Uint64
	recordAccessRepositoryCalls          atomic.Uint64
	recordAccessSQLStatements            atomic.Uint64
	selectedRootRPCSQLStatements         atomic.Uint64
	recordAccessRPCSQLStatements         atomic.Uint64
	queryRepositoryCalls                 atomic.Uint64
	querySQLStatements                   atomic.Uint64
	queryRPCSQLStatements                atomic.Uint64
	queryHistoryRepositoryCalls          atomic.Uint64
	queryHistorySQLStatements            atomic.Uint64
	queryHistoryRPCSQLStatements         atomic.Uint64
	relatedCountRepositoryCalls          atomic.Uint64
	relatedCountSQLStatements            atomic.Uint64
	relatedCountRPCSQLStatements         atomic.Uint64
	createDefaultPreviewRepositoryCalls  atomic.Uint64
	createDefaultPreviewSQLStatements    atomic.Uint64
	createDefaultPreviewRPCSQLStatements atomic.Uint64
	recycleBinRepositoryCalls            atomic.Uint64
	recycleBinSQLStatements              atomic.Uint64
	recycleBinRPCSQLStatements           atomic.Uint64
	recycleBinRestoreRepositoryCalls     atomic.Uint64
	recycleBinRestoreSQLStatements       atomic.Uint64
	recycleBinRestoreRPCSQLStatements    atomic.Uint64
	recycleBinPurgeRepositoryCalls       atomic.Uint64
	recycleBinPurgeSQLStatements         atomic.Uint64
	recycleBinPurgeRPCSQLStatements      atomic.Uint64
	recordViewReadRepositoryCalls        atomic.Uint64
	recordViewReadSQLStatements          atomic.Uint64
	recordViewReadRPCSQLStatements       atomic.Uint64
	attachmentSourceRepositoryCalls      atomic.Uint64
	attachmentSourceSQLStatements        atomic.Uint64
	attachmentSourceRPCSQLStatements     atomic.Uint64
	mutateRPCSQLStatements               atomic.Uint64
	mutationStatusRPCSQLStatements       atomic.Uint64
	identityRPCSQLStatements             atomic.Uint64
	effectivePermissionRPCSQLStatements  atomic.Uint64
	rpcProfiles                          map[string]*runtimeRPCProfile

	repositoryCalls        *prometheus.Desc
	sqlStatements          *prometheus.Desc
	rpcSQLStatements       *prometheus.Desc
	rpcProfileObservations *prometheus.Desc
	rpcProfileDuration     *prometheus.Desc
	rpcProfileSQL          *prometheus.Desc
	rpcProfileSQLDuration  *prometheus.Desc
	poolMaxOpen            *prometheus.Desc
	poolOpen               *prometheus.Desc
	poolInUse              *prometheus.Desc
	poolIdle               *prometheus.Desc
	poolWaitCount          *prometheus.Desc
	poolWaitSeconds        *prometheus.Desc
}

func newRecordsMetrics(pool *sql.DB) *recordsMetrics {
	metrics := &recordsMetrics{
		pool: pool,
		rpcProfiles: map[string]*runtimeRPCProfile{
			runtimeOperationIdentity:                newRuntimeRPCProfile(runtimeOperationIdentity),
			runtimeOperationAppMetadataV3:           newRuntimeRPCProfile(runtimeOperationAppMetadataV3),
			runtimeOperationEffectivePermissions:    newRuntimeRPCProfile(runtimeOperationEffectivePermissions),
			runtimeOperationSearchRecords:           newRuntimeRPCProfile(runtimeOperationSearchRecords),
			runtimeOperationSearchScopeOrder:        newRuntimeRPCProfile(runtimeOperationSearchScopeOrder),
			runtimeOperationExecuteFlow:             newRuntimeRPCProfile(runtimeOperationExecuteFlow),
			runtimeOperationPreviewCatalogAction:    newRuntimeRPCProfile(runtimeOperationPreviewCatalogAction),
			runtimeOperationExecuteApproval:         newRuntimeRPCProfile(runtimeOperationExecuteApproval),
			runtimeOperationReadApproval:            newRuntimeRPCProfile(runtimeOperationReadApproval),
			runtimeOperationPermissionRead:          newRuntimeRPCProfile(runtimeOperationPermissionRead),
			runtimeOperationPermissionSave:          newRuntimeRPCProfile(runtimeOperationPermissionSave),
			runtimeOperationPermissionStatus:        newRuntimeRPCProfile(runtimeOperationPermissionStatus),
			recordsOperationSelectedRoot:            newRuntimeRPCProfile(recordsOperationSelectedRoot),
			recordsOperationAccess:                  newRuntimeRPCProfile(recordsOperationAccess),
			recordsOperationQuery:                   newRuntimeRPCProfile(recordsOperationQuery),
			recordsOperationQueryHistory:            newRuntimeRPCProfile(recordsOperationQueryHistory),
			recordsOperationRelatedCount:            newRuntimeRPCProfile(recordsOperationRelatedCount),
			recordsOperationCreateDefaultPreview:    newRuntimeRPCProfile(recordsOperationCreateDefaultPreview),
			recordsOperationRecycleBin:              newRuntimeRPCProfile(recordsOperationRecycleBin),
			recordsOperationRecycleBinRestore:       newRuntimeRPCProfile(recordsOperationRecycleBinRestore),
			recordsOperationRecycleBinPurge:         newRuntimeRPCProfile(recordsOperationRecycleBinPurge),
			recordsOperationRecordViewRead:          newRuntimeRPCProfile(recordsOperationRecordViewRead),
			recordsOperationAttachmentSourceResolve: newRuntimeRPCProfile(recordsOperationAttachmentSourceResolve),
			recordsOperationMutate:                  newRuntimeRPCProfile(recordsOperationMutate),
			recordsOperationMutationStatus:          newRuntimeRPCProfile(recordsOperationMutationStatus),
		},
		repositoryCalls: prometheus.NewDesc(
			"nexuscrm_runtime_records_repository_calls_total",
			"Runtime Records operations that reached the neutral repository adapter.",
			[]string{"operation"}, nil,
		),
		sqlStatements: prometheus.NewDesc(
			"nexuscrm_runtime_records_sql_statements_total",
			"Context-propagating SQL statement attempts made inside a Runtime Records repository call.",
			[]string{"operation"}, nil,
		),
		rpcSQLStatements: prometheus.NewDesc(
			"nexuscrm_runtime_rpc_sql_statements_total",
			"Context-propagating SQL statement attempts across one complete admitted Runtime RPC.",
			[]string{"operation"}, nil,
		),
		rpcProfileObservations: prometheus.NewDesc(
			"nexuscrm_runtime_rpc_profile_observations_total",
			"Runtime RPC observations split around the Runtime SecurityAdmission boundary.",
			[]string{"operation", "phase"}, nil,
		),
		rpcProfileDuration: prometheus.NewDesc(
			"nexuscrm_runtime_rpc_profile_duration_seconds_total",
			"Cumulative Runtime RPC wall time split around the Runtime SecurityAdmission boundary.",
			[]string{"operation", "phase"}, nil,
		),
		rpcProfileSQL: prometheus.NewDesc(
			"nexuscrm_runtime_rpc_profile_sql_statements_total",
			"Context-propagating SQL statement attempts split around the Runtime SecurityAdmission boundary.",
			[]string{"operation", "phase"}, nil,
		),
		rpcProfileSQLDuration: prometheus.NewDesc(
			"nexuscrm_runtime_rpc_profile_sql_duration_seconds_total",
			"Cumulative context-propagating SQL wall time split around the Runtime SecurityAdmission boundary.",
			[]string{"operation", "phase"}, nil,
		),
		poolMaxOpen:     prometheus.NewDesc("nexuscrm_runtime_db_pool_max_open_connections", "Configured Runtime database pool maximum.", nil, nil),
		poolOpen:        prometheus.NewDesc("nexuscrm_runtime_db_pool_open_connections", "Current open Runtime database connections.", nil, nil),
		poolInUse:       prometheus.NewDesc("nexuscrm_runtime_db_pool_in_use_connections", "Current in-use Runtime database connections.", nil, nil),
		poolIdle:        prometheus.NewDesc("nexuscrm_runtime_db_pool_idle_connections", "Current idle Runtime database connections.", nil, nil),
		poolWaitCount:   prometheus.NewDesc("nexuscrm_runtime_db_pool_wait_count_total", "Runtime database pool waits since process start.", nil, nil),
		poolWaitSeconds: prometheus.NewDesc("nexuscrm_runtime_db_pool_wait_duration_seconds_total", "Runtime database pool wait time since process start.", nil, nil),
	}
	return metrics
}

func (m *recordsMetrics) sqlObservationInterceptor() grpc.UnaryServerInterceptor {
	return func(ctx context.Context, request any, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (any, error) {
		operation, observed := runtimeMetricOperation(info)
		if !observed {
			return handler(ctx, request)
		}
		observedCtx, sqlCounter := telemetry.WithSQLStatementCounter(ctx)
		observedCtx = context.WithValue(observedCtx, runtimeRPCProfileContextKey{}, m.rpcProfile(operation))
		started := time.Now()
		response, err := handler(observedCtx, request)
		m.observeRPCPhase(operation, runtimeRPCPhaseTotal, time.Since(started))
		m.observeRPCSQL(operation, runtimeRPCPhaseTotal, sqlCounter)
		return response, err
	}
}

// postSecurityObservationInterceptor is installed immediately after
// SecurityAdmission. Comparing it with the outer total observation measures
// the existing admission boundary without duplicating any authority logic.
func (m *recordsMetrics) postSecurityObservationInterceptor() grpc.UnaryServerInterceptor {
	return func(ctx context.Context, request any, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (any, error) {
		operation, observed := runtimeMetricOperation(info)
		if !observed {
			return handler(ctx, request)
		}
		observedCtx, sqlCounter := telemetry.WithSQLStatementCounter(ctx)
		started := time.Now()
		response, err := handler(observedCtx, request)
		m.observeRPCPhase(operation, runtimeRPCPhasePostSecurityAdmission, time.Since(started))
		m.observeRPCSQL(operation, runtimeRPCPhasePostSecurityAdmission, sqlCounter)
		return response, err
	}
}

func (m *recordsMetrics) sqlObservationStreamInterceptor() grpc.StreamServerInterceptor {
	return func(server any, stream grpc.ServerStream, info *grpc.StreamServerInfo, handler grpc.StreamHandler) error {
		if stream == nil {
			return handler(server, stream)
		}
		fullMethod := ""
		if info != nil {
			fullMethod = info.FullMethod
		}
		operation, observed := runtimeMetricOperationName(fullMethod)
		if !observed {
			return handler(server, stream)
		}
		observedCtx, sqlCounter := telemetry.WithSQLStatementCounter(stream.Context())
		observedCtx = context.WithValue(observedCtx, runtimeRPCProfileContextKey{}, m.rpcProfile(operation))
		started := time.Now()
		err := handler(server, &runtimeContextServerStream{ServerStream: stream, ctx: observedCtx})
		m.observeRPCPhase(operation, runtimeRPCPhaseTotal, time.Since(started))
		m.observeRPCSQL(operation, runtimeRPCPhaseTotal, sqlCounter)
		return err
	}
}

func (m *recordsMetrics) postSecurityObservationStreamInterceptor() grpc.StreamServerInterceptor {
	return func(server any, stream grpc.ServerStream, info *grpc.StreamServerInfo, handler grpc.StreamHandler) error {
		if stream == nil {
			return handler(server, stream)
		}
		fullMethod := ""
		if info != nil {
			fullMethod = info.FullMethod
		}
		operation, observed := runtimeMetricOperationName(fullMethod)
		if !observed {
			return handler(server, stream)
		}
		observedCtx, sqlCounter := telemetry.WithSQLStatementCounter(stream.Context())
		started := time.Now()
		err := handler(server, &runtimeContextServerStream{ServerStream: stream, ctx: observedCtx})
		m.observeRPCPhase(operation, runtimeRPCPhasePostSecurityAdmission, time.Since(started))
		m.observeRPCSQL(operation, runtimeRPCPhasePostSecurityAdmission, sqlCounter)
		return err
	}
}

func runtimeMetricOperation(info *grpc.UnaryServerInfo) (string, bool) {
	if info == nil {
		return "", false
	}
	return runtimeMetricOperationName(info.FullMethod)
}

func runtimeMetricOperationName(fullMethod string) (string, bool) {
	switch fullMethod {
	case runtimev1.IdentityContextService_Exchange_FullMethodName:
		return runtimeOperationIdentity, true
	case catalogv1.AppMetadataViewV3Service_GetAppMetadataViewV3_FullMethodName:
		return runtimeOperationAppMetadataV3, true
	case runtimev1.EffectivePermissionService_ReadEffectivePermissions_FullMethodName:
		return runtimeOperationEffectivePermissions, true
	case runtimev1.RecordSearchService_SearchRecords_FullMethodName:
		return runtimeOperationSearchRecords, true
	case runtimev1.RecordSearchService_ReadSearchScopeOrder_FullMethodName:
		return runtimeOperationSearchScopeOrder, true
	case runtimev1.FlowExecutionService_ExecuteFlow_FullMethodName:
		return runtimeOperationExecuteFlow, true
	case runtimev1.FlowExecutionService_PreviewCatalogAction_FullMethodName:
		return runtimeOperationPreviewCatalogAction, true
	case runtimev1.ApprovalService_ExecuteApproval_FullMethodName:
		return runtimeOperationExecuteApproval, true
	case runtimev1.ApprovalService_ReadApproval_FullMethodName:
		return runtimeOperationReadApproval, true
	case runtimev1.PermissionAuthoringService_ReadPermissionAuthoringSnapshot_FullMethodName:
		return runtimeOperationPermissionRead, true
	case runtimev1.PermissionAuthoringService_SavePermissionAuthoring_FullMethodName:
		return runtimeOperationPermissionSave, true
	case runtimev1.PermissionAuthoringService_GetPermissionAuthoringStatus_FullMethodName:
		return runtimeOperationPermissionStatus, true
	case runtimev1.RecordsService_ReadSelectedRoot_FullMethodName:
		return recordsOperationSelectedRoot, true
	case runtimev1.RecordsService_EvaluateRecordAccess_FullMethodName:
		return recordsOperationAccess, true
	case runtimev1.RecordQueryService_QueryRecords_FullMethodName:
		return recordsOperationQuery, true
	case runtimev1.RecordQueryService_QueryHistory_FullMethodName:
		return recordsOperationQueryHistory, true
	case runtimev1.RelatedRecordCountService_CountRelatedRecords_FullMethodName:
		return recordsOperationRelatedCount, true
	case runtimev1.RecordCreateDefaultPreviewService_PreviewCreateDefaults_FullMethodName:
		return recordsOperationCreateDefaultPreview, true
	case runtimev1.RecycleBinReadService_ListRecycleBin_FullMethodName:
		return recordsOperationRecycleBin, true
	case runtimev1.RecycleBinMutationService_RestoreRecycleBin_FullMethodName:
		return recordsOperationRecycleBinRestore, true
	case runtimev1.RecycleBinMutationService_PurgeRecycleBin_FullMethodName:
		return recordsOperationRecycleBinPurge, true
	case runtimev1.RecordViewReadService_ReadRecordView_FullMethodName:
		return recordsOperationRecordViewRead, true
	case runtimev1.RecordAttachmentSourceResolutionService_ResolveAttachmentSources_FullMethodName:
		return recordsOperationAttachmentSourceResolve, true
	case runtimev1.RecordMutationsService_MutateRecords_FullMethodName:
		return recordsOperationMutate, true
	case runtimev1.RecordMutationsService_GetMutationStatus_FullMethodName:
		return recordsOperationMutationStatus, true
	default:
		return "", false
	}
}

func (m *recordsMetrics) observeRPCSQL(operation, phase string, counter *telemetry.SQLStatementCounter) {
	if m == nil {
		return
	}
	sqlStatements := counter.Count()
	sqlNanos := counter.TotalDuration().Nanoseconds()
	if sqlNanos < 0 {
		sqlNanos = 0
	}
	profile := m.rpcProfile(operation)
	if profile == nil {
		return
	}
	phaseProfile := profile.phases[phase]
	if phaseProfile == nil {
		return
	}
	phaseProfile.sqlStatements.Add(sqlStatements)
	phaseProfile.sqlNanos.Add(uint64(sqlNanos))
	if phase != runtimeRPCPhaseTotal {
		return
	}
	switch operation {
	case runtimeOperationIdentity:
		m.identityRPCSQLStatements.Add(sqlStatements)
	case runtimeOperationEffectivePermissions:
		m.effectivePermissionRPCSQLStatements.Add(sqlStatements)
	case recordsOperationSelectedRoot:
		m.selectedRootRPCSQLStatements.Add(sqlStatements)
	case recordsOperationAccess:
		m.recordAccessRPCSQLStatements.Add(sqlStatements)
	case recordsOperationQuery:
		m.queryRPCSQLStatements.Add(sqlStatements)
	case recordsOperationQueryHistory:
		m.queryHistoryRPCSQLStatements.Add(sqlStatements)
	case recordsOperationRelatedCount:
		m.relatedCountRPCSQLStatements.Add(sqlStatements)
	case recordsOperationCreateDefaultPreview:
		m.createDefaultPreviewRPCSQLStatements.Add(sqlStatements)
	case recordsOperationRecycleBin:
		m.recycleBinRPCSQLStatements.Add(sqlStatements)
	case recordsOperationRecycleBinRestore:
		m.recycleBinRestoreRPCSQLStatements.Add(sqlStatements)
	case recordsOperationRecycleBinPurge:
		m.recycleBinPurgeRPCSQLStatements.Add(sqlStatements)
	case recordsOperationRecordViewRead:
		m.recordViewReadRPCSQLStatements.Add(sqlStatements)
	case recordsOperationAttachmentSourceResolve:
		m.attachmentSourceRPCSQLStatements.Add(sqlStatements)
	case recordsOperationMutate:
		m.mutateRPCSQLStatements.Add(sqlStatements)
	case recordsOperationMutationStatus:
		m.mutationStatusRPCSQLStatements.Add(sqlStatements)
	}
}

func (m *recordsMetrics) rpcProfile(operation string) *runtimeRPCProfile {
	if m == nil {
		return nil
	}
	return m.rpcProfiles[operation]
}

func (m *recordsMetrics) observeRPCPhase(operation, phase string, elapsed time.Duration) {
	if m == nil {
		return
	}
	profile := m.rpcProfile(operation)
	if profile == nil {
		return
	}
	phaseProfile := profile.phases[phase]
	if phaseProfile == nil {
		return
	}
	nanos := elapsed.Nanoseconds()
	if nanos < 0 {
		nanos = 0
	}
	phaseProfile.observations.Add(1)
	phaseProfile.nanos.Add(uint64(nanos))
}

type runtimeRPCProfileContextKey struct{}

func beginRuntimeRPCProfilePhase(ctx context.Context, phase string) (context.Context, func()) {
	if ctx == nil {
		return ctx, func() {}
	}
	profile, _ := ctx.Value(runtimeRPCProfileContextKey{}).(*runtimeRPCProfile)
	if profile == nil || profile.phases[phase] == nil {
		return ctx, func() {}
	}
	observedCtx, sqlCounter := telemetry.WithSQLStatementCounter(ctx)
	started := time.Now()
	return observedCtx, func() {
		phaseProfile := profile.phases[phase]
		elapsed := time.Since(started).Nanoseconds()
		if elapsed < 0 {
			elapsed = 0
		}
		sqlNanos := sqlCounter.TotalDuration().Nanoseconds()
		if sqlNanos < 0 {
			sqlNanos = 0
		}
		phaseProfile.observations.Add(1)
		phaseProfile.nanos.Add(uint64(elapsed))
		phaseProfile.sqlStatements.Add(sqlCounter.Count())
		phaseProfile.sqlNanos.Add(uint64(sqlNanos))
		if perftiming.Enabled(ctx) {
			perftiming.Event(ctx, "runtime_rpc_phase_timing", started, nil).
				Str("operation", profile.operation).
				Str("phase", phase).
				Uint64("sql_statements", sqlCounter.Count()).
				Int64("sql_elapsed_ms", sqlCounter.TotalDuration().Milliseconds()).
				Int64("sql_max_ms", sqlCounter.MaxDuration().Milliseconds()).
				Msg("Runtime RPC phase timing")
		}
	}
}

func (m *recordsMetrics) observeRepository(operation string, sqlStatements uint64) {
	if m == nil {
		return
	}
	switch operation {
	case recordsOperationSelectedRoot:
		m.selectedRootRepositoryCalls.Add(1)
		m.selectedRootSQLStatements.Add(sqlStatements)
	case recordsOperationAccess:
		m.recordAccessRepositoryCalls.Add(1)
		m.recordAccessSQLStatements.Add(sqlStatements)
	case recordsOperationQuery:
		m.queryRepositoryCalls.Add(1)
		m.querySQLStatements.Add(sqlStatements)
	case recordsOperationQueryHistory:
		m.queryHistoryRepositoryCalls.Add(1)
		m.queryHistorySQLStatements.Add(sqlStatements)
	case recordsOperationRelatedCount:
		m.relatedCountRepositoryCalls.Add(1)
		m.relatedCountSQLStatements.Add(sqlStatements)
	case recordsOperationCreateDefaultPreview:
		m.createDefaultPreviewRepositoryCalls.Add(1)
		m.createDefaultPreviewSQLStatements.Add(sqlStatements)
	case recordsOperationRecycleBin:
		m.recycleBinRepositoryCalls.Add(1)
		m.recycleBinSQLStatements.Add(sqlStatements)
	case recordsOperationRecycleBinRestore:
		m.recycleBinRestoreRepositoryCalls.Add(1)
		m.recycleBinRestoreSQLStatements.Add(sqlStatements)
	case recordsOperationRecycleBinPurge:
		m.recycleBinPurgeRepositoryCalls.Add(1)
		m.recycleBinPurgeSQLStatements.Add(sqlStatements)
	case recordsOperationRecordViewRead:
		m.recordViewReadRepositoryCalls.Add(1)
		m.recordViewReadSQLStatements.Add(sqlStatements)
	case recordsOperationAttachmentSourceResolve:
		m.attachmentSourceRepositoryCalls.Add(1)
		m.attachmentSourceSQLStatements.Add(sqlStatements)
	}
}

func (m *recordsMetrics) Describe(ch chan<- *prometheus.Desc) {
	ch <- m.repositoryCalls
	ch <- m.sqlStatements
	ch <- m.rpcSQLStatements
	ch <- m.rpcProfileObservations
	ch <- m.rpcProfileDuration
	ch <- m.rpcProfileSQL
	ch <- m.rpcProfileSQLDuration
	ch <- m.poolMaxOpen
	ch <- m.poolOpen
	ch <- m.poolInUse
	ch <- m.poolIdle
	ch <- m.poolWaitCount
	ch <- m.poolWaitSeconds
}

func (m *recordsMetrics) Collect(ch chan<- prometheus.Metric) {
	ch <- prometheus.MustNewConstMetric(m.repositoryCalls, prometheus.CounterValue, float64(m.selectedRootRepositoryCalls.Load()), recordsOperationSelectedRoot)
	ch <- prometheus.MustNewConstMetric(m.repositoryCalls, prometheus.CounterValue, float64(m.recordAccessRepositoryCalls.Load()), recordsOperationAccess)
	ch <- prometheus.MustNewConstMetric(m.repositoryCalls, prometheus.CounterValue, float64(m.queryRepositoryCalls.Load()), recordsOperationQuery)
	ch <- prometheus.MustNewConstMetric(m.repositoryCalls, prometheus.CounterValue, float64(m.queryHistoryRepositoryCalls.Load()), recordsOperationQueryHistory)
	ch <- prometheus.MustNewConstMetric(m.repositoryCalls, prometheus.CounterValue, float64(m.relatedCountRepositoryCalls.Load()), recordsOperationRelatedCount)
	ch <- prometheus.MustNewConstMetric(m.repositoryCalls, prometheus.CounterValue, float64(m.createDefaultPreviewRepositoryCalls.Load()), recordsOperationCreateDefaultPreview)
	ch <- prometheus.MustNewConstMetric(m.repositoryCalls, prometheus.CounterValue, float64(m.recycleBinRepositoryCalls.Load()), recordsOperationRecycleBin)
	ch <- prometheus.MustNewConstMetric(m.repositoryCalls, prometheus.CounterValue, float64(m.recycleBinRestoreRepositoryCalls.Load()), recordsOperationRecycleBinRestore)
	ch <- prometheus.MustNewConstMetric(m.repositoryCalls, prometheus.CounterValue, float64(m.recycleBinPurgeRepositoryCalls.Load()), recordsOperationRecycleBinPurge)
	ch <- prometheus.MustNewConstMetric(m.repositoryCalls, prometheus.CounterValue, float64(m.recordViewReadRepositoryCalls.Load()), recordsOperationRecordViewRead)
	ch <- prometheus.MustNewConstMetric(m.repositoryCalls, prometheus.CounterValue, float64(m.attachmentSourceRepositoryCalls.Load()), recordsOperationAttachmentSourceResolve)
	ch <- prometheus.MustNewConstMetric(m.sqlStatements, prometheus.CounterValue, float64(m.selectedRootSQLStatements.Load()), recordsOperationSelectedRoot)
	ch <- prometheus.MustNewConstMetric(m.sqlStatements, prometheus.CounterValue, float64(m.recordAccessSQLStatements.Load()), recordsOperationAccess)
	ch <- prometheus.MustNewConstMetric(m.sqlStatements, prometheus.CounterValue, float64(m.querySQLStatements.Load()), recordsOperationQuery)
	ch <- prometheus.MustNewConstMetric(m.sqlStatements, prometheus.CounterValue, float64(m.queryHistorySQLStatements.Load()), recordsOperationQueryHistory)
	ch <- prometheus.MustNewConstMetric(m.sqlStatements, prometheus.CounterValue, float64(m.relatedCountSQLStatements.Load()), recordsOperationRelatedCount)
	ch <- prometheus.MustNewConstMetric(m.sqlStatements, prometheus.CounterValue, float64(m.createDefaultPreviewSQLStatements.Load()), recordsOperationCreateDefaultPreview)
	ch <- prometheus.MustNewConstMetric(m.sqlStatements, prometheus.CounterValue, float64(m.recycleBinSQLStatements.Load()), recordsOperationRecycleBin)
	ch <- prometheus.MustNewConstMetric(m.sqlStatements, prometheus.CounterValue, float64(m.recycleBinRestoreSQLStatements.Load()), recordsOperationRecycleBinRestore)
	ch <- prometheus.MustNewConstMetric(m.sqlStatements, prometheus.CounterValue, float64(m.recycleBinPurgeSQLStatements.Load()), recordsOperationRecycleBinPurge)
	ch <- prometheus.MustNewConstMetric(m.sqlStatements, prometheus.CounterValue, float64(m.recordViewReadSQLStatements.Load()), recordsOperationRecordViewRead)
	ch <- prometheus.MustNewConstMetric(m.sqlStatements, prometheus.CounterValue, float64(m.attachmentSourceSQLStatements.Load()), recordsOperationAttachmentSourceResolve)
	ch <- prometheus.MustNewConstMetric(m.rpcSQLStatements, prometheus.CounterValue, float64(m.identityRPCSQLStatements.Load()), runtimeOperationIdentity)
	ch <- prometheus.MustNewConstMetric(m.rpcSQLStatements, prometheus.CounterValue, float64(m.effectivePermissionRPCSQLStatements.Load()), runtimeOperationEffectivePermissions)
	ch <- prometheus.MustNewConstMetric(m.rpcSQLStatements, prometheus.CounterValue, float64(m.selectedRootRPCSQLStatements.Load()), recordsOperationSelectedRoot)
	ch <- prometheus.MustNewConstMetric(m.rpcSQLStatements, prometheus.CounterValue, float64(m.recordAccessRPCSQLStatements.Load()), recordsOperationAccess)
	ch <- prometheus.MustNewConstMetric(m.rpcSQLStatements, prometheus.CounterValue, float64(m.queryRPCSQLStatements.Load()), recordsOperationQuery)
	ch <- prometheus.MustNewConstMetric(m.rpcSQLStatements, prometheus.CounterValue, float64(m.queryHistoryRPCSQLStatements.Load()), recordsOperationQueryHistory)
	ch <- prometheus.MustNewConstMetric(m.rpcSQLStatements, prometheus.CounterValue, float64(m.relatedCountRPCSQLStatements.Load()), recordsOperationRelatedCount)
	ch <- prometheus.MustNewConstMetric(m.rpcSQLStatements, prometheus.CounterValue, float64(m.createDefaultPreviewRPCSQLStatements.Load()), recordsOperationCreateDefaultPreview)
	ch <- prometheus.MustNewConstMetric(m.rpcSQLStatements, prometheus.CounterValue, float64(m.recycleBinRPCSQLStatements.Load()), recordsOperationRecycleBin)
	ch <- prometheus.MustNewConstMetric(m.rpcSQLStatements, prometheus.CounterValue, float64(m.recycleBinRestoreRPCSQLStatements.Load()), recordsOperationRecycleBinRestore)
	ch <- prometheus.MustNewConstMetric(m.rpcSQLStatements, prometheus.CounterValue, float64(m.recycleBinPurgeRPCSQLStatements.Load()), recordsOperationRecycleBinPurge)
	ch <- prometheus.MustNewConstMetric(m.rpcSQLStatements, prometheus.CounterValue, float64(m.recordViewReadRPCSQLStatements.Load()), recordsOperationRecordViewRead)
	ch <- prometheus.MustNewConstMetric(m.rpcSQLStatements, prometheus.CounterValue, float64(m.attachmentSourceRPCSQLStatements.Load()), recordsOperationAttachmentSourceResolve)
	ch <- prometheus.MustNewConstMetric(m.rpcSQLStatements, prometheus.CounterValue, float64(m.mutateRPCSQLStatements.Load()), recordsOperationMutate)
	ch <- prometheus.MustNewConstMetric(m.rpcSQLStatements, prometheus.CounterValue, float64(m.mutationStatusRPCSQLStatements.Load()), recordsOperationMutationStatus)
	for operation, profile := range m.rpcProfiles {
		total := profile.phases[runtimeRPCPhaseTotal]
		if total == nil || total.observations.Load() == 0 {
			continue
		}
		for phase, phaseProfile := range profile.phases {
			if phaseProfile.observations.Load() == 0 {
				continue
			}
			ch <- prometheus.MustNewConstMetric(
				m.rpcProfileObservations, prometheus.CounterValue,
				float64(phaseProfile.observations.Load()), operation, phase,
			)
			ch <- prometheus.MustNewConstMetric(
				m.rpcProfileDuration, prometheus.CounterValue,
				float64(phaseProfile.nanos.Load())/float64(time.Second), operation, phase,
			)
			ch <- prometheus.MustNewConstMetric(
				m.rpcProfileSQL, prometheus.CounterValue,
				float64(phaseProfile.sqlStatements.Load()), operation, phase,
			)
			ch <- prometheus.MustNewConstMetric(
				m.rpcProfileSQLDuration, prometheus.CounterValue,
				float64(phaseProfile.sqlNanos.Load())/float64(time.Second), operation, phase,
			)
		}
	}
	if m.pool == nil {
		return
	}
	stats := m.pool.Stats()
	ch <- prometheus.MustNewConstMetric(m.poolMaxOpen, prometheus.GaugeValue, float64(stats.MaxOpenConnections))
	ch <- prometheus.MustNewConstMetric(m.poolOpen, prometheus.GaugeValue, float64(stats.OpenConnections))
	ch <- prometheus.MustNewConstMetric(m.poolInUse, prometheus.GaugeValue, float64(stats.InUse))
	ch <- prometheus.MustNewConstMetric(m.poolIdle, prometheus.GaugeValue, float64(stats.Idle))
	ch <- prometheus.MustNewConstMetric(m.poolWaitCount, prometheus.CounterValue, float64(stats.WaitCount))
	ch <- prometheus.MustNewConstMetric(m.poolWaitSeconds, prometheus.CounterValue, stats.WaitDuration.Seconds())
}
