package s2slog

import (
	"fmt"

	"go.uber.org/zap"
	"go.uber.org/zap/zapcore"
)

type S2SZapLogger struct {
	l *zap.Logger
}

func FromZapLogger(l *zap.Logger) *S2SZapLogger {
	return &S2SZapLogger{
		l: l,
	}
}

func (s *S2SZapLogger) Debug(args ...interface{})            { log(s.l.Debug, args...) }
func (s *S2SZapLogger) Debugf(m string, args ...interface{}) { log(s.l.Debug, format(m, args...)) }
func (s *S2SZapLogger) Debugln(args ...interface{})          { log(s.l.Debug, args...) }
func (s *S2SZapLogger) Error(args ...interface{})            { log(s.l.Error, args...) }
func (s *S2SZapLogger) Errorf(m string, args ...interface{}) { log(s.l.Error, format(m, args...)) }
func (s *S2SZapLogger) Errorln(args ...interface{})          { log(s.l.Error, args...) }
func (s *S2SZapLogger) Fatal(args ...interface{})            { log(s.l.Fatal, args...) }
func (s *S2SZapLogger) Fatalf(m string, args ...interface{}) { log(s.l.Fatal, format(m, args...)) }
func (s *S2SZapLogger) Fatalln(args ...interface{})          { log(s.l.Fatal, args...) }
func (s *S2SZapLogger) Info(args ...interface{})             { log(s.l.Info, args...) }
func (s *S2SZapLogger) Infof(m string, args ...interface{})  { log(s.l.Info, format(m, args...)) }
func (s *S2SZapLogger) Infoln(args ...interface{})           { log(s.l.Info, args...) }
func (s *S2SZapLogger) Panic(args ...interface{})            { log(s.l.Panic, args...) }
func (s *S2SZapLogger) Panicf(m string, args ...interface{}) { log(s.l.Panic, format(m, args...)) }
func (s *S2SZapLogger) Panicln(args ...interface{})          { log(s.l.Panic, args...) }
func (s *S2SZapLogger) Print(args ...interface{})            { log(s.l.Info, args...) }
func (s *S2SZapLogger) Printf(m string, args ...interface{}) { log(s.l.Info, format(m, args...)) }
func (s *S2SZapLogger) Println(args ...interface{})          { log(s.l.Info, args...) }
func (s *S2SZapLogger) Warn(args ...interface{})             { log(s.l.Warn, args...) }
func (s *S2SZapLogger) Warnf(m string, args ...interface{})  { log(s.l.Warn, format(m, args...)) }
func (s *S2SZapLogger) Warnln(args ...interface{})           { log(s.l.Warn, args...) }

func format(m string, args ...interface{}) string {
	return fmt.Sprintf(m, args...)
}

func log(f func(msg string, fields ...zap.Field), ifaces ...interface{}) {
	// must be at least 1 arg
	if len(ifaces) < 1 {
		return
	}
	logMsg := &s2sLogMsg{ifaces}
	if str, ok := ifaces[0].(string); ok {
		if len(ifaces) > 1 {
			f(str, zap.Array("s2s", &s2sLogMsg{ifaces[1:]}))
		} else {
			f(str)
		}
	} else {
		f("s2s logging event (details under 's2s' key)", zap.Array("s2s", logMsg))
	}
}

type s2sLogMsg struct {
	ifaces []interface{}
}

func (m *s2sLogMsg) MarshalLogArray(ae zapcore.ArrayEncoder) error {
	for _, m := range m.ifaces {
		if err := ae.AppendReflected(m); err != nil {
			return err
		}
	}
	return nil
}
