package logs

import (
	
	
	
	
)

// Logger is an alternative implementation of [slog.Logger] that allows
// configuring time source and program counter for log records.
//
// Unlike [slog.Logger], Logger does not provide convenience methods like
// Info, Debug, Warn, or Error. Instead, all logging is done through the
// Log method with explicit context, level, and attributes.
type Logger struct {
	// handler is the underlying log handler.
	handler slog.Handler

	// timeNow is a function that returns the current time.
	// If timeNow is nil, [slog.Record.Time] will not be set.
	timeNow func() time.Time

	// capturePC indicates whether the logger should capture program
	// counter.
	capturePC bool

	// skipPC is the number of stack frames to skip for program counter.
	// If skipPC is negative, [slog.Record.PC] will not be set.
	skipPC int
}

// New creates a new [Logger] with the given non-nil [slog.Handler].
func ( slog.Handler) *Logger {
	return &Logger{
		handler:   ,
		timeNow:   time.Now,
		capturePC: true,
	}
}

// Handler returns the underlying [slog.Handler].
func ( *Logger) () slog.Handler {
	return .handler
}

// WithHandler returns a [Logger] that uses the given [slog.Handler].
func ( *Logger) ( slog.Handler) *Logger {
	 := *
	.handler = 
	return &
}

// WithAttrs returns a [Logger] that includes the given attributes in each
// output operation.
func ( *Logger) ( ...slog.Attr) *Logger {
	if len() == 0 {
		return 
	}
	return .WithHandler(.handler.WithAttrs())
}

// WithGroup returns a [Logger] that starts a group. The keys of all attributes
// added to the [Logger] will be qualified by the given name.
//
// If name is empty, WithGroup returns the receiver.
func ( *Logger) ( string) *Logger {
	if  == "" {
		return 
	}
	return .WithHandler(.handler.WithGroup())
}

// WithTime returns a [Logger] that uses the given time function.
// If now is nil, [slog.Record.Time] will not be set.
func ( *Logger) ( func() time.Time) *Logger {
	if  == nil && .timeNow == nil {
		return 
	}
	 := *
	.timeNow = 
	return &
}

// WithCapturePC returns a [Logger] with program counter capture toggled.
// If v is false, [slog.Record.PC] will not be set regardless of WithSkipPC.
func ( *Logger) ( bool) *Logger {
	if .capturePC ==  {
		return 
	}
	 := *
	.capturePC = 
	return &
}

// WithSkipPC returns a [Logger] that skips additional stack frames.
// If n is non-positive, WithSkipPC returns the receiver.
func ( *Logger) ( int) *Logger {
	if  <= 0 || .skipPC < 0 {
		return 
	}
	 := *
	.skipPC += 
	if .skipPC < 0 {
		.skipPC = -1
	}
	return &
}

// Enabled reports whether l emits log records at the given context and level.
func ( *Logger) ( context.Context,  slog.Level) bool {
	return .handler.Enabled(, )
}

// Log emits a log record with the given level, message, and attributes.
//
// By default, [slog.Record.PC] will be set to the caller’s program counter
// and [slog.Record.Time] will be set to the current time. Use WithCapturePC,
// WithSkipPC and WithTime methods to change this behavior.
func ( *Logger) ( context.Context,  slog.Level,  string,  ...slog.Attr) {
	if !.Enabled(, ) {
		return
	}
	_ = .output(, , , )
}

func ( *Logger) (
	 context.Context,
	 slog.Level,
	 string,
	 []slog.Attr,
) error {
	var  time.Time
	if .timeNow != nil {
		 = .timeNow()
	}

	var  uintptr
	if .capturePC && .skipPC >= 0 {
		// Skip 4 stack frames to log actual caller:
		//   - runtime.Callers
		//   - callerPC
		//   - this function
		//   - this function’s caller (Logger.Log or Writer.Write)
		 := .skipPC + 4
		if  >= 0 {
			 = callerPC()
		}
	}

	 := slog.NewRecord(, , , )
	.AddAttrs(...)
	return .handler.Handle(, )
}

// callerPC returns the program counter at the given stack depth.
func ( int) uintptr {
	var  [1]uintptr
	_ = runtime.Callers(, [:])
	return [0]
}