package zap

import (
	"go.uber.org/zap"

	"go.uber.org/zap/zapcore"
	"amuz.es/src/infra/goutils/logger"
	"amuz.es/src/infra/goutils/logger/rotater"
)

var (
	defaultWriter             logger.RotateSyncer
	defaultErrorOutputOptions []zap.Option
	nopCloser                 = func() (err error) { return }
)

func init() {
	zap.RedirectStdLog(zap.L())
}

func replaceGlobalLogger(newOne *zap.Logger) {
	zap.ReplaceGlobals(newOne)
	zap.RedirectStdLog(newOne)
}

func Init(
	verbose bool,
	formatter zapcore.Encoder,
	mainLogName, logFilename, logDir string,
	rotateOption []rotater.Option,
	logLevel zapcore.Level,
	additionalOptions ...zap.Option,
) (*zap.SugaredLogger, error) {
	level := zap.NewAtomicLevelAt(logLevel)
	if defaultWriter, err := rotater.NewLogWriter(logFilename, logDir, rotateOption...); err != nil {
		return nil, err
	} else {
		defaultErrorOutputOptions = []zap.Option{zap.ErrorOutput(defaultWriter)}
		options := defaultErrorOutputOptions
		if verbose {
			options = append(options, zap.AddStacktrace(zap.NewAtomicLevelAt(zap.PanicLevel)))
		}
		// reset log option slice
		options = append(options, additionalOptions...)

		log := initLogger(defaultWriter, mainLogName, formatter, level, options...)

		replaceGlobalLogger(log)
		return log.Sugar(), nil
	}
}

func New(parent *zap.SugaredLogger, moduleName string, options ...zap.Option) *zap.SugaredLogger {
	var subLogger *zap.Logger
	if parent == nil {
		subLogger = zap.L().Named(moduleName)
	} else {
		subLogger = parent.Desugar().Named(moduleName)
	}

	subLogger.WithOptions(options...)

	return subLogger.Sugar()
}

func NewOtherLogger(
	formatter zapcore.Encoder,
	moduleName, logFilename, logDir string,
	rotateOption []rotater.Option,
	logLevel zapcore.Level,
	fields ...zapcore.Field,
) (logger *zap.SugaredLogger, closer func() error, err error) {
	loglevel := zap.NewAtomicLevelAt(logLevel)
	logWriter, err := rotater.NewLogWriter(logFilename, logDir, rotateOption...)
	if err != nil {
		return
	}
	core := zapcore.NewCore(formatter, logWriter, loglevel)
	closer = logWriter.Close
	logger = zap.New(core, defaultErrorOutputOptions...).
		Named(moduleName).With(fields...).Sugar()
	return
}

func NewOtherLoggerWithOption(
	formatter zapcore.Encoder,
	moduleName, logFilename, logDir string,
	rotateOption []rotater.Option,
	logLevel zapcore.Level,
	options []zap.Option,
	fields ...zapcore.Field,
) (logger *zap.SugaredLogger, closer func() error, err error) {
	loglevel := zap.NewAtomicLevelAt(logLevel)
	logWriter, err := rotater.NewLogWriter(logFilename, logDir, rotateOption...)
	if err != nil {
		return
	}
	core := zapcore.NewCore(formatter, logWriter, loglevel)
	closer = logWriter.Close
	options = append(defaultErrorOutputOptions, options...)
	logger = zap.New(core, options...).
		Named(moduleName).With(fields...).Sugar()
	return
}
func initLogger(
	writer zapcore.WriteSyncer,
	moduleName string,
	formatter zapcore.Encoder,
	level zap.AtomicLevel,
	options ...zap.Option,
) *zap.Logger {
	core := zapcore.NewCore(formatter, writer, level)
	return zap.New(core, options...).Named(moduleName)
}
