package records

import (
	"context"
	"sync"
	"time"

	"github.com/nexuscrm/kernel/pkg/telemetry"
	"github.com/nexuscrm/shared/pkg/constants"
	semantic "github.com/pingcap-inc/nexuscrm-contract/pkg/runtime/recordmutationv1"
	zlog "github.com/rs/zerolog/log"
)

const (
	runtimeMutationPhaseAccept            = "accept"
	runtimeMutationPhasePrepare           = "prepare"
	runtimeMutationPhaseOperationGuard    = "operation_guard"
	runtimeMutationPhaseBusinessEffect    = "business_effect"
	runtimeMutationPhaseOperationComplete = "operation_complete"
	runtimeMutationPhaseFinalize          = "finalize"
)

var runtimeMutationProfilePhases = [...]string{
	runtimeMutationPhaseAccept,
	runtimeMutationPhasePrepare,
	runtimeMutationPhaseOperationGuard,
	runtimeMutationPhaseBusinessEffect,
	runtimeMutationPhaseOperationComplete,
	runtimeMutationPhaseFinalize,
}

type runtimeMutationPhaseProfileKey struct{}

type runtimeMutationPhaseObservation struct {
	calls      uint64
	elapsed    time.Duration
	sql        uint64
	sqlElapsed time.Duration
	sqlMax     time.Duration
}

// runtimeMutationPhaseProfile is deliberately one bounded log event per coarse
// command. It does not record SQL text, fields, record IDs, or metadata. The
// fixed phase set lets a production-shaped run distinguish durable command
// bookkeeping from the owned business effect without enabling the verbose
// Flow/SQL event stream that previously distorted Test2 latency.
type runtimeMutationPhaseProfile struct {
	mu       sync.Mutex
	started  time.Time
	request  semantic.Request
	totalSQL *telemetry.SQLStatementCounter
	phases   map[string]runtimeMutationPhaseObservation
}

func withRuntimeMutationPhaseProfile(ctx context.Context, request semantic.Request) (context.Context, *runtimeMutationPhaseProfile) {
	observedCtx, totalSQL := telemetry.WithSQLStatementCounter(ctx)
	profile := &runtimeMutationPhaseProfile{
		started: time.Now(), request: request, totalSQL: totalSQL,
		phases: make(map[string]runtimeMutationPhaseObservation, len(runtimeMutationProfilePhases)),
	}
	return context.WithValue(observedCtx, runtimeMutationPhaseProfileKey{}, profile), profile
}

func beginRuntimeMutationPhase(ctx context.Context, phase string) (context.Context, func()) {
	profile, _ := ctx.Value(runtimeMutationPhaseProfileKey{}).(*runtimeMutationPhaseProfile)
	if profile == nil {
		return ctx, func() {}
	}
	observedCtx, sqlCounter := telemetry.WithSQLStatementCounter(ctx)
	started := time.Now()
	var once sync.Once
	return observedCtx, func() {
		once.Do(func() {
			observation := runtimeMutationPhaseObservation{
				calls: 1, elapsed: time.Since(started), sql: sqlCounter.Count(),
				sqlElapsed: sqlCounter.TotalDuration(), sqlMax: sqlCounter.MaxDuration(),
			}
			profile.mu.Lock()
			current := profile.phases[phase]
			current.calls += observation.calls
			current.elapsed += observation.elapsed
			current.sql += observation.sql
			current.sqlElapsed += observation.sqlElapsed
			if observation.sqlMax > current.sqlMax {
				current.sqlMax = observation.sqlMax
			}
			profile.phases[phase] = current
			profile.mu.Unlock()
		})
	}
}

func (p *runtimeMutationPhaseProfile) log(ctx context.Context) {
	if p == nil || p.totalSQL == nil {
		return
	}
	p.mu.Lock()
	phases := make(map[string]runtimeMutationPhaseObservation, len(p.phases))
	for phase, observation := range p.phases {
		phases[phase] = observation
	}
	p.mu.Unlock()

	event := zlog.Debug().
		Str("event", "runtime_record_mutation_phase_profile").
		Str("request_id", requestIDFromContext(ctx)).
		Str("use_case", string(p.request.UseCase)).
		Str("batch_mode", string(p.request.BatchMode)).
		Int("operations", len(p.request.Operations)).
		Uint64("total_sql", p.totalSQL.Count()).
		Int64("total_sql_elapsed_ms", p.totalSQL.TotalDuration().Milliseconds()).
		Int64("total_sql_max_ms", p.totalSQL.MaxDuration().Milliseconds()).
		Int64("total_elapsed_ms", time.Since(p.started).Milliseconds())
	for _, phase := range runtimeMutationProfilePhases {
		observation := phases[phase]
		event = event.
			Uint64(phase+"_calls", observation.calls).
			Uint64(phase+"_sql", observation.sql).
			Int64(phase+"_sql_elapsed_ms", observation.sqlElapsed.Milliseconds()).
			Int64(phase+"_sql_max_ms", observation.sqlMax.Milliseconds()).
			Int64(phase+"_elapsed_ms", observation.elapsed.Milliseconds())
	}
	event.Msg("Runtime record mutation phase profile")
}

func requestIDFromContext(ctx context.Context) string {
	requestID, _ := ctx.Value(constants.ContextKeyRequestID).(string)
	return requestID
}
