package logger

import (
	"errors"
	"fmt"
	"go.uber.org/zap"
	"go.uber.org/zap/zapcore"
	"gopkg.in/natefinch/lumberjack.v2"
	"path"
	"strings"
	"time"
)

var rootLoggerInited bool
var RootLogger *zap.Logger
var InTest bool

const (
	LogIDBgWorker = "[bgWorker]"
	LogIDMod      = "[mod]"
	LogIDTrace    = "[trace]"
)

func InitLogger(logPath string, level zapcore.Level) (*zap.Logger, error) {
	if rootLoggerInited {
		return nil, errors.New("rootLogger already inited!")
	}
	rootLoggerInited = true

	writeSyncer := zapcore.AddSync(&lumberjack.Logger{
		Filename:   path.Join(logPath, "autoscaler.log"),
		MaxSize:    200, // megabytes
		MaxBackups: 1,   // only 1 backup, so 400M disk usage total.
	})

	encoderConfig := zap.NewProductionEncoderConfig()
	encoderConfig.EncodeLevel = func(level zapcore.Level, enc zapcore.PrimitiveArrayEncoder) {
		enc.AppendString("[" + level.CapitalString() + "]")
	}
	encoderConfig.EncodeTime = func(t time.Time, enc zapcore.PrimitiveArrayEncoder) {
		enc.AppendString("[" + t.Format("2006-01-02 15:04:05.000Z07:00") + "]")
	}
	encoderConfig.EncodeCaller = func(caller zapcore.EntryCaller, enc zapcore.PrimitiveArrayEncoder) {
		enc.AppendString("[" + caller.TrimmedPath() + "]")
	}

	fileLoggerEncoder := zapcore.NewConsoleEncoder(encoderConfig)
	fileLoggerCore := zapcore.NewCore(fileLoggerEncoder, writeSyncer, level)

	stdLogger, err := zap.Config{
		Level:       zap.NewAtomicLevelAt(level),
		Development: false,
		Sampling: &zap.SamplingConfig{
			Initial:    100,
			Thereafter: 100,
		},
		Encoding:         "console",
		EncoderConfig:    encoderConfig,
		OutputPaths:      []string{"stdout"},
		ErrorOutputPaths: []string{"stderr", path.Join(logPath, "autoscaler_err.log")},
	}.Build()
	if err != nil {
		return nil, err
	}

	core := zapcore.NewTee(stdLogger.Core(), fileLoggerCore)
	logger := zap.New(core, zap.AddCaller(), zap.AddStacktrace(zapcore.ErrorLevel))

	RootLogger = logger
	return logger, nil
}

func GetLogLevel(levelStr string) zapcore.Level {
	switch strings.ToLower(levelStr) {
	case "debug":
		return zapcore.DebugLevel
	case "info":
		return zapcore.InfoLevel
	case "warn":
		return zapcore.WarnLevel
	case "error":
		return zapcore.ErrorLevel
	case "fatal":
		return zapcore.FatalLevel
	default:
		panic(fmt.Errorf("invalid log level: %v", levelStr))
		return zapcore.InvalidLevel
	}
}
