// Package logging собирает slog-логгер по настройкам из конфига.
package logging

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

// New возвращает slog-логгер с указанным уровнем и форматом ("json"|"text").
func New(level, format string) *slog.Logger {
	return NewTo(os.Stdout, level, format)
}

// NewErr — тот же логгер, что New, но в stderr.
//
// Нужен командам CLI: stdout у них занят отчётом человеку, и лог в том же
// потоке сделал бы отчёт неразбираемым — а именно его человек перенаправляет в
// файл и читает глазами.
func NewErr(level, format string) *slog.Logger {
	return NewTo(os.Stderr, level, format)
}

// NewTo — общая форма: приёмник вывода параметром, как у log.New и
// slog.NewTextHandler. New и NewErr — тонкие обёртки над ней; отдельные имена
// существуют ради читаемости места вызова, а не ради разного поведения.
func NewTo(w io.Writer, level, format string) *slog.Logger {
	opts := &slog.HandlerOptions{Level: parseLevel(level), ReplaceAttr: utcTime}

	var handler slog.Handler
	if strings.EqualFold(format, "text") {
		handler = slog.NewTextHandler(w, opts)
	} else {
		handler = slog.NewJSONHandler(w, opts)
	}
	return slog.New(handler)
}

// NewStderr — JSON-логгер в stderr для фатальных ошибок старта, когда основной
// логгер ещё не собран (конфиг не прочитан). Уровень и формат брать неоткуда,
// поэтому они фиксированы — этим он и отличается от NewErr.
func NewStderr() *slog.Logger {
	return NewTo(os.Stderr, "info", "json")
}

// utcTime приводит метку времени записи к UTC: однозначный порядок событий и
// лексикографическая сортировка.
func utcTime(groups []string, a slog.Attr) slog.Attr {
	if len(groups) == 0 && a.Key == slog.TimeKey {
		if t, ok := a.Value.Any().(time.Time); ok {
			a.Value = slog.TimeValue(t.UTC())
		}
	}
	return a
}

func parseLevel(level string) slog.Level {
	switch strings.ToLower(level) {
	case "debug":
		return slog.LevelDebug
	case "warn", "warning":
		return slog.LevelWarn
	case "error":
		return slog.LevelError
	default:
		return slog.LevelInfo
	}
}
