// Copyright 2024 schukai GmbH
// SPDX-License-Identifier: AGPL-3.0

package jobqueue

import (
	"go.uber.org/zap"
	"log"
	"os"
	"sync"
)

// Logger interface, that your library's users have to implement
type Logger interface {
	Info(msg string, keysAndValues ...interface{})
	Warn(msg string, keysAndValues ...interface{})
	Error(msg string, keysAndValues ...interface{})

	Trace(msg string, keysAndValues ...interface{})
}

type DefaultLogger struct {
	*log.Logger
}

func (l *DefaultLogger) Info(msg string, keysAndValues ...interface{}) {
	l.Print("INFO: "+msg, keysAndValues)
}

func (l *DefaultLogger) Error(msg string, keysAndValues ...interface{}) {
	l.Print("ERROR: "+msg, keysAndValues)
}

func (l *DefaultLogger) Warn(msg string, keysAndValues ...interface{}) {
	l.Print("WARN: "+msg, keysAndValues)
}

func (l *DefaultLogger) Trace(msg string, keysAndValues ...interface{}) {
	l.Print("DEBUG: "+msg, keysAndValues)
}

// Ensure DefaultLogger satisfies the Logger interface.
var _ Logger = (*DefaultLogger)(nil)

var (
	internalLogger Logger
	mu             sync.Mutex
)

func init() {
	internalLogger = &DefaultLogger{
		Logger: log.New(os.Stderr, "", log.LstdFlags),
	}
}

// SetLogger sets a custom logger
func SetLogger(l Logger) {
	mu.Lock()
	defer mu.Unlock()
	internalLogger = l
}

// Info logs an informational message to the current logger
func Info(msg string, keysAndValues ...interface{}) {
	mu.Lock()
	defer mu.Unlock()
	if internalLogger == nil {
		return
	}
	internalLogger.Info(msg, keysAndValues...)
}

// Error logs an error message to the current logger
func Error(msg string, keysAndValues ...interface{}) {
	mu.Lock()
	defer mu.Unlock()
	if internalLogger == nil {
		return
	}
	internalLogger.Error(msg, keysAndValues...)
}

// Warn logs a warning message to the current logger
func Warn(msg string, keysAndValues ...interface{}) {
	mu.Lock()
	defer mu.Unlock()
	if internalLogger == nil {
		return
	}
	internalLogger.Warn(msg, keysAndValues...)
}

func Trace(msg string, keysAndValues ...interface{}) {
	mu.Lock()
	defer mu.Unlock()
	if internalLogger == nil {
		return
	}
	internalLogger.Trace(msg, keysAndValues...)
}

type ZapAdapter struct {
	logger *zap.Logger
	mu     sync.Mutex
}

func NewZapAdapter(logger *zap.Logger) *ZapAdapter {
	return &ZapAdapter{
		logger: logger,
	}
}

func (l *ZapAdapter) Info(msg string, keysAndValues ...interface{}) {
	if l.logger == nil {
		return
	}
	l.logger.Info(msg, zap.Any("Info", keysAndValues))
}

func (l *ZapAdapter) Warn(msg string, keysAndValues ...interface{}) {
	if l.logger == nil {
		return
	}
	l.logger.Warn(msg, zap.Any("Warn", keysAndValues))
}

func (l *ZapAdapter) Error(msg string, keysAndValues ...interface{}) {
	if l.logger == nil {
		return
	}
	l.logger.Error(msg, zap.Any("Error", keysAndValues))
}

func (l *ZapAdapter) Trace(msg string, keysAndValues ...interface{}) {
	if l.logger == nil {
		return
	}
	l.logger.Debug(msg, zap.Any("Debug", keysAndValues))
}

// Ensure ZapAdapter satisfies the Logger interface.
var _ Logger = (*ZapAdapter)(nil)

// SetZapLogger sets a custom logger
func SetZapLogger(logger *zap.Logger) {
	mu.Lock()
	defer mu.Unlock()
	internalLogger = NewZapAdapter(logger)
}