// Package logging provides slog-based structured logging with context integration.
//
// This package standardizes logging across all services with:
//   - Environment-aware formatting (JSON for production, text for development)
//   - Request-scoped loggers with context propagation
//   - Convenience methods for common logging patterns
//
// Usage:
//
//	// Create a logger based on environment
//	logger := logging.New(logging.Config{
//	    Level:       logging.LevelInfo,
//	    Format:      logging.FormatJSON,
//	    Environment: "production",
//	})
//
//	// Or use convenience constructors
//	logger := logging.NewDevelopment() // text format, debug level
//	logger := logging.NewProduction()  // JSON format, info level
package logging

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

// Level represents the logging level.
type Level int

const (
	LevelDebug Level = iota
	LevelInfo
	LevelWarn
	LevelError
)

// String returns the string representation of the level.
func (l Level) String() string {
	switch l {
	case LevelDebug:
		return "debug"
	case LevelInfo:
		return "info"
	case LevelWarn:
		return "warn"
	case LevelError:
		return "error"
	default:
		return "info"
	}
}

// ParseLevel parses a string into a Level.
// Returns LevelInfo if the string is not recognized.
func ParseLevel(s string) Level {
	switch strings.ToLower(strings.TrimSpace(s)) {
	case "debug":
		return LevelDebug
	case "info":
		return LevelInfo
	case "warn", "warning":
		return LevelWarn
	case "error":
		return LevelError
	default:
		return LevelInfo
	}
}

func (l Level) toSlog() slog.Level {
	switch l {
	case LevelDebug:
		return slog.LevelDebug
	case LevelInfo:
		return slog.LevelInfo
	case LevelWarn:
		return slog.LevelWarn
	case LevelError:
		return slog.LevelError
	default:
		return slog.LevelInfo
	}
}

// Format represents the output format.
type Format int

const (
	FormatJSON Format = iota
	FormatText
)

// String returns the string representation of the format.
func (f Format) String() string {
	switch f {
	case FormatJSON:
		return "json"
	case FormatText:
		return "text"
	default:
		return "json"
	}
}

// ParseFormat parses a string into a Format.
// Returns FormatJSON if the string is not recognized.
func ParseFormat(s string) Format {
	switch strings.ToLower(strings.TrimSpace(s)) {
	case "text", "console":
		return FormatText
	case "json":
		return FormatJSON
	default:
		return FormatJSON
	}
}

// Config holds the logger configuration.
type Config struct {
	// Level sets the minimum log level.
	// Default: LevelInfo
	Level Level

	// Format sets the output format.
	// Default: FormatJSON
	Format Format

	// Output sets the output writer.
	// Default: os.Stdout
	Output io.Writer

	// AddSource adds source file and line number to log entries.
	// Default: false
	AddSource bool

	// Environment determines default format if not specified.
	// "development" uses text format, others use JSON.
	Environment string
}

// Logger wraps slog.Logger with additional convenience methods.
type Logger struct {
	*slog.Logger
}

// New creates a new Logger with the given configuration.
func New(cfg Config) *Logger {
	if cfg.Output == nil {
		cfg.Output = os.Stdout
	}

	// Auto-detect format based on environment if not explicitly set
	format := cfg.Format
	if cfg.Environment == "development" && format == FormatJSON {
		format = FormatText
	}

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

	var handler slog.Handler
	switch format {
	case FormatText:
		handler = slog.NewTextHandler(cfg.Output, opts)
	default:
		handler = slog.NewJSONHandler(cfg.Output, opts)
	}

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

// NewDevelopment creates a logger configured for development.
// Uses text format, debug level, and includes source location.
func NewDevelopment() *Logger {
	return New(Config{
		Level:     LevelDebug,
		Format:    FormatText,
		AddSource: true,
	})
}

// NewProduction creates a logger configured for production.
// Uses JSON format and info level.
func NewProduction() *Logger {
	return New(Config{
		Level:  LevelInfo,
		Format: FormatJSON,
	})
}

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

// WithGroup returns a new Logger with the given group name.
func (l *Logger) WithGroup(name string) *Logger {
	return &Logger{
		Logger: l.Logger.WithGroup(name),
	}
}

// WithError returns a new Logger with the error attribute.
func (l *Logger) WithError(err error) *Logger {
	if err == nil {
		return l
	}
	return l.With("error", err.Error())
}

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

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

// Nop returns a logger that discards all output.
func Nop() *Logger {
	return New(Config{
		Output: io.Discard,
		Level:  LevelError,
	})
}

// Default returns the default logger configured for the current environment.
// Uses APP_ENVIRONMENT env var to determine format.
func Default() *Logger {
	env := os.Getenv("APP_ENVIRONMENT")
	if env == "development" || env == "" {
		return NewDevelopment()
	}
	return NewProduction()
}
