package perftiming

import (
	"context"
	"time"

	"github.com/nexuscrm/shared/pkg/constants"
	"github.com/rs/zerolog"
	zlog "github.com/rs/zerolog/log"
)

type observerContextKey struct{}

// Observation is a process-local diagnostic timing sample. It is not a wire,
// capability, or application contract.
type Observation struct {
	Name     string
	Duration time.Duration
}

// Observer receives opt-in diagnostic timing samples synchronously on the
// request goroutine. Callers must keep observers non-blocking.
type Observer interface {
	ObservePerformance(Observation)
}

type ObserverFunc func(Observation)

func (f ObserverFunc) ObservePerformance(observation Observation) {
	if f != nil {
		f(observation)
	}
}

// WithObserver installs a process-local diagnostic observer. It is intended
// for profiling and tests; production request semantics never depend on it.
func WithObserver(ctx context.Context, observer Observer) context.Context {
	if observer == nil {
		return ctx
	}
	return context.WithValue(ctx, observerContextKey{}, observer)
}

// StartObservation avoids a clock read when no observer is installed.
func StartObservation(ctx context.Context) time.Time {
	if observer, _ := ctx.Value(observerContextKey{}).(Observer); observer == nil {
		return time.Time{}
	}
	return time.Now()
}

// Observe publishes a completed diagnostic stage when observation is enabled.
func Observe(ctx context.Context, name string, started time.Time) {
	if started.IsZero() {
		return
	}
	observer, _ := ctx.Value(observerContextKey{}).(Observer)
	if observer == nil {
		return
	}
	observer.ObservePerformance(Observation{Name: name, Duration: time.Since(started)})
}

// WithEnabled marks a request context as opted in to verbose performance timing.
func WithEnabled(ctx context.Context) context.Context {
	return context.WithValue(ctx, constants.ContextKeyPerfTiming, true)
}

// Enabled reports whether verbose performance timing is enabled for this request.
func Enabled(ctx context.Context) bool {
	v, _ := ctx.Value(constants.ContextKeyPerfTiming).(bool)
	return v
}

// RequestID extracts the request id used to correlate timing logs.
func RequestID(ctx context.Context) string {
	id, _ := ctx.Value(constants.ContextKeyRequestID).(string)
	return id
}

func Status(err error) string {
	if err != nil {
		return "error"
	}
	return "success"
}

// Event returns an info/error zerolog event with the standard timing fields.
func Event(ctx context.Context, name string, started time.Time, err error) *zerolog.Event {
	return EventWithStatus(ctx, name, started, Status(err), err)
}

// EventWithStatus returns a timing event while preserving caller-specific status labels.
func EventWithStatus(ctx context.Context, name string, started time.Time, status string, err error) *zerolog.Event {
	return baseEvent(ctx, name, status, err, true).
		Int64("elapsed_ms", time.Since(started).Milliseconds())
}

// BaseEvent returns an info/error zerolog event with standard event correlation fields.
func BaseEvent(ctx context.Context, name string, status string, err error) *zerolog.Event {
	return baseEvent(ctx, name, status, err, true)
}

// InfoEventWithStatus returns an info-level timing event with caller-specific status labels.
func InfoEventWithStatus(ctx context.Context, name string, started time.Time, status string, err error) *zerolog.Event {
	return baseEvent(ctx, name, status, err, false).
		Int64("elapsed_ms", time.Since(started).Milliseconds())
}

func baseEvent(ctx context.Context, name string, status string, err error, errorLevel bool) *zerolog.Event {
	if status == "" {
		status = Status(err)
	}
	event := zlog.Info()
	if err != nil && errorLevel {
		event = zlog.Error().Err(err)
	} else if err != nil {
		event = event.Err(err)
	}
	return event.
		Str("event", name).
		Str("request_id", RequestID(ctx)).
		Str("status", status)
}
