package gologger

import (
	"strings"
	"time"
)

const (
	NoLog = iota
	Error
	Warn
	Info
	Debug
	Trace
)

var logLevelMap map[string]int = map[string]int{
	"off":   NoLog,
	"error": Error,
	"warn":  Warn,
	"info":  Info,
	"debug": Debug,
	"trace": Trace,
}
var loggerMap map[string]Logger = map[string]Logger{}
var appenderFactoryMap map[string]func(LogAppenderConfig) LoggerAppender = map[string]func(LogAppenderConfig) LoggerAppender{}
var appenders map[string]LoggerAppender = map[string]LoggerAppender{}
var loggerConfig LoggersConfig

type LogAppenderConfig struct {
	Type    string                 `json:"type"`
	Options map[string]interface{} `json:"options"`
}
type LogConfig struct {
	Level     string   `json:"level"`
	Appenders []string `json:"appenders"`
}

type LoggersConfig struct {
	Appenders  map[string]LogAppenderConfig `json:"appenders"`
	Categories map[string]LogConfig         `json:"categories"`
}
type Logger struct {
	category  string
	level     int
	appenders []LoggerAppender
}

type LogEvent struct {
	Category string
	Ts       time.Time
	Level    int
	Data     []interface{}
}

type LoggerAppender interface {
	GetName() string
	Append(logEvent LogEvent)
}

var consoleAppender LoggerAppender = &ConsoleAppender{}
var defaultLogger = &Logger{

	level:     Error,
	appenders: []LoggerAppender{consoleAppender},
}

func (l *Logger) log(Level int, msg []interface{}) {

	if Level <= l.level {
		now := time.Now()
		logEvent := LogEvent{l.category, now, Level, msg}
		for _, appender := range l.appenders {
			appender.Append(logEvent)
		}
		// l.Appender.Append(logEvent)
		// fmt.Println(now.Format("2006-01-02 15:04:05"), " ", l.Name, ": ", msg)
	}
}

func (l *Logger) Error(msg ...interface{}) {
	l.log(Error, msg)
}

func (l *Logger) Warn(msg ...interface{}) {
	l.log(Warn, msg)
}

func (l *Logger) Info(msg ...interface{}) {
	l.log(Info, msg)
}

func (l *Logger) Debug(msg ...interface{}) {
	l.log(Debug, msg)
}

func (l *Logger) Trace(msg ...interface{}) {
	l.log(Trace, msg)
}
func GetLogger(name string) Logger {
	if logger, ok := loggerMap[name]; ok {
		return logger
	} else {
		logConfig, ok := loggerConfig.Categories[name]
		if ok {
			return makeLogger(name, logConfig)
		}
		if name == "default" {
			return *defaultLogger
		}
		ret := GetLogger("default")
		ret.category = name
		return ret
	}
}

func makeLogger(name string, config LogConfig) Logger {
	logger := &Logger{category: name}
	levelstr := strings.ToLower(config.Level)
	logger.level = logLevelMap[levelstr]

	if config.Appenders == nil || len(config.Appenders) == 0 {
		logger.appenders = []LoggerAppender{consoleAppender}
	} else {
		logger.appenders = make([]LoggerAppender, len(config.Appenders))
		for i, appenderName := range config.Appenders {
			logger.appenders[i] = appenders[appenderName]
		}
	}
	loggerMap[name] = *logger
	return *logger
}
func Configure(config LoggersConfig) {
	loggerConfig = config
	for name, appenderConfig := range loggerConfig.Appenders {
		appenderFactory, ok := appenderFactoryMap[appenderConfig.Type]
		if ok {
			appenders[name] = appenderFactory(appenderConfig)
		} else {
			appenders[name] = &ConsoleAppender{}
		}
	}
	for name, _ := range loggerConfig.Categories {
		GetLogger(name)
	}
}

func RegistAppender(typeName string, appenderCreatCb func(LogAppenderConfig) LoggerAppender) {

	appenderFactoryMap[typeName] = appenderCreatCb
}
