package logging

import (
	"io"
	"log/slog"
	"os"
	"time"
)

// Logger wraps slog.Logger with convenience methods and standard fields.
type Logger struct {
	*slog.Logger
	config Config
}

// New creates a new Logger from the given config.
func New(cfg Config) *Logger {
	return NewWithWriter(cfg, os.Stdout)
}

// NewWithWriter creates a new Logger writing to the given writer.
func NewWithWriter(cfg Config, w io.Writer) *Logger {
	var handler slog.Handler

	opts := &slog.HandlerOptions{
		Level:     cfg.Level.SlogLevel(),
		AddSource: cfg.AddSource,
	}

	if cfg.Format == FormatText {
		handler = slog.NewTextHandler(w, opts)
	} else {
		handler = slog.NewJSONHandler(w, opts)
	}

	// Wrap with redacting handler if enabled
	if cfg.RedactEnabled {
		handler = NewRedactingHandler(handler)
	}

	return &Logger{
		Logger: slog.New(handler),
		config: cfg,
	}
}

// With returns a new Logger with the given attributes.
func (l *Logger) With(args ...any) *Logger {
	return &Logger{
		Logger: l.Logger.With(args...),
		config: l.config,
	}
}

// WithComponent returns a new Logger with the component field set.
func (l *Logger) WithComponent(name string) *Logger {
	return l.With(FieldComponent, name)
}

// WithHandler returns a new Logger with the handler field set.
func (l *Logger) WithHandler(name string) *Logger {
	return l.With(FieldHandler, name)
}

// WithService returns a new Logger with the service field set.
func (l *Logger) WithService(name string) *Logger {
	return l.With(FieldService, name)
}

// WithWorker returns a new Logger with the worker field set.
func (l *Logger) WithWorker(name string) *Logger {
	return l.With(FieldWorker, name)
}

// WithAdapter returns a new Logger with the adapter field set.
func (l *Logger) WithAdapter(name string) *Logger {
	return l.With(FieldAdapter, name)
}

// WithRequestID returns a new Logger with the request_id field set.
func (l *Logger) WithRequestID(id string) *Logger {
	return l.With(FieldRequestID, id)
}

// WithProjectID returns a new Logger with the project_id field set.
func (l *Logger) WithProjectID(id string) *Logger {
	return l.With(FieldProjectID, id)
}

// WithUserID returns a new Logger with the user_id field set.
func (l *Logger) WithUserID(id string) *Logger {
	return l.With(FieldUserID, id)
}

// WithError returns a new Logger with the error field set.
// Always use "error" as the field name, never "err" or "e".
func (l *Logger) WithError(err error) *Logger {
	if err == nil {
		return l
	}
	return l.With(FieldError, err.Error())
}

// WithOperation returns a new Logger with the operation field set.
func (l *Logger) WithOperation(name string) *Logger {
	return l.With(FieldOperation, name)
}

// Timed returns a function that logs the duration when called.
// Usage: defer log.Timed("operation_name")()
func (l *Logger) Timed(operation string) func() {
	start := time.Now()
	return func() {
		duration := time.Since(start)
		l.Info("operation completed",
			FieldOperation, operation,
			FieldDuration, duration.Milliseconds(),
		)
	}
}

// TimedWithLevel returns a function that logs the duration at the specified level.
// Usage: defer log.TimedWithLevel("operation_name", LevelDebug)()
func (l *Logger) TimedWithLevel(operation string, level Level) func() {
	start := time.Now()
	return func() {
		duration := time.Since(start)
		msg := "operation completed"
		args := []any{FieldOperation, operation, FieldDuration, duration.Milliseconds()}

		switch level {
		case LevelDebug:
			l.Debug(msg, args...)
		case LevelInfo:
			l.Info(msg, args...)
		case LevelWarn:
			l.Warn(msg, args...)
		case LevelError:
			l.Error(msg, args...)
		}
	}
}

// Slog returns the underlying slog.Logger for compatibility.
func (l *Logger) Slog() *slog.Logger {
	return l.Logger
}

// Config returns the logger's configuration.
func (l *Logger) Config() Config {
	return l.config
}
