package logger

import (
	"runtime"
	"strings"
)

const (
	timeLayout = "2006-01-02 15:04:05.000000"

	// Log level consts
	debugLevel = "D"
	infoLevel  = "I"
	warnLevel  = "W"
	errLevel   = "E"
	fatalLevel = "F"
)

/*************************************************/

// CallerType - enum of objects which can call logger
type CallerType string

// caller type consts
const (
	AppCaller = "A"
	LibCaller = "L"
)

/*************************************************/

// LayerType - enum of logical layers
type LayerType string

// Layer consts
const (
	TransportLayer   = "T"
	ApplicationLayer = "A"
	OtherLayer       = "O"
)

/*************************************************/

// ModuleType - enum of backend modules type
type ModuleType string

// Module types consts
const (
	ServerType     = "S"
	ClientType     = "C"
	StandaloneType = "A"
	OtherType      = "O"
)

/*************************************************/

type Logger interface {
	Debugf(format string, args ...interface{})
	Debug(args ...interface{})
	Debugln(args ...interface{})

	Infof(format string, args ...interface{})
	Info(args ...interface{})
	Infoln(args ...interface{})

	Printf(format string, args ...interface{})
	Print(args ...interface{})
	Println(args ...interface{})

	Warnf(format string, args ...interface{})
	Warn(args ...interface{})
	Warnln(args ...interface{})

	Errorf(format string, args ...interface{})
	Error(args ...interface{})
	Errorln(args ...interface{})

	Fatalf(format string, args ...interface{})
	Fatal(args ...interface{})
	Fatalln(args ...interface{})
}

type loggerImpl struct {
	callerIdent string
}

type LoggerInfo struct {
	CallerType CallerType
	Layer      LayerType
	Name       string
	ModuleType ModuleType

	callerFile string
}

func generateCallerIdent(info *LoggerInfo) string {
	needFullIdent := info.Name != ""
	var ident strings.Builder
	if info.CallerType != "" {
		ident.WriteString(string(info.CallerType))
	} else { // Default value
		ident.WriteString(AppCaller)
	}

	if info.Layer != "" {
		ident.WriteString(string(info.Layer))
	} else if needFullIdent {
		ident.WriteString(string(OtherLayer))
	}

	if info.ModuleType != "" {
		ident.WriteString(string(info.ModuleType))
	} else if needFullIdent {
		ident.WriteString(string(OtherType))
	}

	if needFullIdent {
		ident.WriteString("-" + string(info.Name))
	}
	return ident.String()
}

func NewLogger(info *LoggerInfo) Logger {
	logger := &loggerImpl{}
	logger.callerIdent = generateCallerIdent(&LoggerInfo{
		CallerType: LibCaller,
		Layer:      OtherLayer,
		ModuleType: OtherType,
	})
	//TODO: Add automatic detection
	_, file, _, ok := runtime.Caller(1)
	if ok {
		info.callerFile = file
	}
	logger.startIdentify(info)
	logger.callerIdent = generateCallerIdent(info)
	return logger
}
