package logger

import (
	"encoding/json"
	"fmt"
	"io"
	"log"
	"os"
)

// Logger matches newrelic.Logger to allow implementations to be passed to
// internal packages.
type Logger interface {
	Error(msg string, context map[string]interface{})
	Warn(msg string, context map[string]interface{})
	Info(msg string, context map[string]interface{})
	Debug(msg string, context map[string]interface{})
	DebugEnabled() bool
}

// ShimLogger implements Logger and does nothing.
type ShimLogger struct {
	// IsDebugEnabled is useful as it allows DebugEnabled code paths to be
	// tested.
	IsDebugEnabled bool
}

// Error allows ShimLogger to implement Logger.
func (s ShimLogger) Error(string, map[string]interface{}) {}

// Warn allows ShimLogger to implement Logger.
func (s ShimLogger) Warn(string, map[string]interface{}) {}

// Info allows ShimLogger to implement Logger.
func (s ShimLogger) Info(string, map[string]interface{}) {}

// Debug allows ShimLogger to implement Logger.
func (s ShimLogger) Debug(string, map[string]interface{}) {}

// DebugEnabled allows ShimLogger to implement Logger.
func (s ShimLogger) DebugEnabled() bool { return s.IsDebugEnabled }

type logFile struct {
	l       *log.Logger
	doDebug bool
}

// New creates a basic Logger.
func New(w io.Writer, doDebug bool) Logger {
	return &logFile{
		l:       log.New(w, logPid, logFlags),
		doDebug: doDebug,
	}
}

const logFlags = log.Ldate | log.Ltime | log.Lmicroseconds

var (
	logPid = fmt.Sprintf("(%d) ", os.Getpid())
)

func (f *logFile) fire(level, msg string, ctx map[string]interface{}) {
	js, err := json.Marshal(struct {
		Level   string                 `json:"level"`
		Event   string                 `json:"msg"`
		Context map[string]interface{} `json:"context"`
	}{
		level,
		msg,
		ctx,
	})
	if nil == err {
		f.l.Print(string(js))
	} else {
		f.l.Printf("unable to marshal log entry: %v", err)
	}
}

func (f *logFile) Error(msg string, ctx map[string]interface{}) {
	f.fire("error", msg, ctx)
}
func (f *logFile) Warn(msg string, ctx map[string]interface{}) {
	f.fire("warn", msg, ctx)
}
func (f *logFile) Info(msg string, ctx map[string]interface{}) {
	f.fire("info", msg, ctx)
}
func (f *logFile) Debug(msg string, ctx map[string]interface{}) {
	if f.doDebug {
		f.fire("debug", msg, ctx)
	}
}
func (f *logFile) DebugEnabled() bool { return f.doDebug }
