package utils

import (
	"os"

	"go.uber.org/zap"
	"go.uber.org/zap/zapcore"
)

var Log *zap.Logger
func init() {
	if Log == nil {
		InitLogger()
	}
}

// LogLevel represents logging levels
type LogLevel string

const (
	DebugLevel LogLevel = "debug"
	InfoLevel  LogLevel = "info"
	WarnLevel  LogLevel = "warn"
	ErrorLevel LogLevel = "error"
	FatalLevel LogLevel = "fatal"
)

// LoggerConfig holds logger configuration
type LoggerConfig struct {
	Level       LogLevel `json:"level"`
	Development bool     `json:"development"`
	OutputPaths []string `json:"output_paths"`
}

// InitLogger initializes the global logger with default production config
func InitLogger() {
	config := GetDefaultLoggerConfig()
	InitLoggerWithConfig(config)
}

// InitLoggerWithConfig initializes logger with custom configuration
func InitLoggerWithConfig(config LoggerConfig) {
	var zapConfig zap.Config

	if config.Development {
		zapConfig = zap.NewDevelopmentConfig()
		zapConfig.EncoderConfig.EncodeLevel = zapcore.CapitalColorLevelEncoder
	} else {
		zapConfig = zap.NewProductionConfig()
	}

	// Set log level
	switch config.Level {
	case DebugLevel:
		zapConfig.Level = zap.NewAtomicLevelAt(zapcore.DebugLevel)
	case InfoLevel:
		zapConfig.Level = zap.NewAtomicLevelAt(zapcore.InfoLevel)
	case WarnLevel:
		zapConfig.Level = zap.NewAtomicLevelAt(zapcore.WarnLevel)
	case ErrorLevel:
		zapConfig.Level = zap.NewAtomicLevelAt(zapcore.ErrorLevel)
	case FatalLevel:
		zapConfig.Level = zap.NewAtomicLevelAt(zapcore.FatalLevel)
	default:
		zapConfig.Level = zap.NewAtomicLevelAt(zapcore.InfoLevel)
	}

	// Format time for readability
	zapConfig.EncoderConfig.EncodeTime = zapcore.ISO8601TimeEncoder
	zapConfig.EncoderConfig.TimeKey = "timestamp"
	zapConfig.EncoderConfig.MessageKey = "message"
	zapConfig.EncoderConfig.LevelKey = "level"
	zapConfig.EncoderConfig.CallerKey = "caller"

	// Set output paths
	if len(config.OutputPaths) > 0 {
		zapConfig.OutputPaths = config.OutputPaths
	}

	var err error
	Log, err = zapConfig.Build()
	if err != nil {
		panic(err)
	}

	zap.ReplaceGlobals(Log)
}

// GetDefaultLoggerConfig returns default logger configuration
func GetDefaultLoggerConfig() LoggerConfig {
	level := InfoLevel
	development := false

	// Check environment variables
	if envLevel := os.Getenv("LOG_LEVEL"); envLevel != "" {
		level = LogLevel(envLevel)
	}

	if os.Getenv("LOG_DEVELOPMENT") == "true" {
		development = true
	}

	return LoggerConfig{
		Level:       level,
		Development: development,
		OutputPaths: []string{"stdout"},
	}
}

// Debug logs a debug message
func Debug(msg string, fields ...zap.Field) {
	if Log != nil {
		Log.Debug(msg, fields...)
	}
}

// Info logs an info message
func Info(msg string, fields ...zap.Field) {
	if Log != nil {
		Log.Info(msg, fields...)
	}
}

// Warn logs a warning message
func Warn(msg string, fields ...zap.Field) {
	if Log != nil {
		Log.Warn(msg, fields...)
	}
}

// Error logs an error message
func Error(msg string, fields ...zap.Field) {
	if Log != nil {
		Log.Error(msg, fields...)
	}
}

// Fatal logs a fatal message and exits
func Fatal(msg string, fields ...zap.Field) {
	if Log != nil {
		Log.Fatal(msg, fields...)
	}
}

// With creates a child logger with additional fields
func With(fields ...zap.Field) *zap.Logger {
	return Log.With(fields...)
}

// Named creates a named logger
func Named(name string) *zap.Logger {
	return Log.Named(name)
}

// Sync flushes any buffered log entries
func Sync() error {
	return Log.Sync()
}

// SetLevel dynamically changes the log level
func SetLevel(level LogLevel) {
	// Note: This requires the logger to be created with zap.NewAtomicLevel()
	// For now, we'll log the level change
	Info("Log level changed", zap.String("new_level", string(level)))
}
