package observability

import (
	"context"
	"time"

	"code.justin.tv/eventbus/controlplane/internal/db"
	"code.justin.tv/eventbus/controlplane/internal/logger"
	"go.uber.org/zap"
	"go.uber.org/zap/zapcore"
)

// dbOperation is a function that performs some database operation and returns
// some contextual logging fields relating to the operation, and any error
// that occurred during the operation's execution
type dbOperation func() ([]zap.Field, error)

// observe performs a database operation. The operation will be timed and operational
// data from the operation will be logged, submitted as a metric, or both depending
// on configuration of the observabilityDB.
func (o *ObservabilityDB) observe(ctx context.Context, action string, f dbOperation) {
	// Do the underlying operation, timed
	start := time.Now()
	opFields, opErr := f() // Do the DB operation
	end := time.Now()

	// Bundle all the data needed for generating a log line and metric(s)
	data := &observabilityData{
		action: action,
		start:  start,
		end:    end,
		fields: opFields,
		err:    opErr,
		logger: logger.FromContext(ctx),
	}

	if o.loggingEnabled {
		log(data)
	}

	if o.metricsEnabled {
		submitMetrics(data)
	}
}

// custom type so we can marshal as an array
type eventStreamsList struct {
	streams []*db.EventStream
}

func (e *eventStreamsList) MarshalLogArray(enc zapcore.ArrayEncoder) error {
	for _, eventStream := range e.streams {
		_ = enc.AppendObject(eventStream)
	}
	return nil
}
