package serverconfig

import (
	"encoding/json"
	"io"
	"strconv"
	"time"

	"bitbucket.org/nemt/nemt-portal-api/infra/logger"
	"github.com/Sirupsen/logrus"
	"github.com/labstack/echo"
	"github.com/labstack/gommon/log"
)

type echoLogger struct {
	*logger.Logger
}

// Printj prints a message in json format
func (l *echoLogger) Printj(data log.JSON) {
	bytes, _ := json.Marshal(data)
	l.Logger.Print(string(bytes))
}

// Debugj logs a debug message in json format
func (l *echoLogger) Debugj(data log.JSON) {
	bytes, _ := json.Marshal(data)
	l.Logger.Debug(string(bytes))
}

// Infoj logs an info message in json format
func (l *echoLogger) Infoj(data log.JSON) {
	bytes, _ := json.Marshal(data)
	l.Logger.Info(string(bytes))
}

// Warnj logs a warning message in json format
func (l *echoLogger) Warnj(data log.JSON) {
	bytes, _ := json.Marshal(data)
	l.Logger.Warn(string(bytes))
}

// Errorj logs an error message in json format
func (l *echoLogger) Errorj(data log.JSON) {
	bytes, _ := json.Marshal(data)
	l.Logger.Error(string(bytes))
}

// Panicj logs a panic message in json format
func (l *echoLogger) Panicj(data log.JSON) {
	bytes, _ := json.Marshal(data)
	l.Logger.Panic(string(bytes))
}

// Fatalj logs a fatal message in json format
func (l *echoLogger) Fatalj(data log.JSON) {
	bytes, _ := json.Marshal(data)
	l.Logger.Fatal(string(bytes))
}

// Level returns the echo logging level
func (l *echoLogger) Level() log.Lvl {
	return log.Lvl(l.Logger.Level)
}

// SetLevel sets the echo logging level
func (l *echoLogger) SetLevel(level log.Lvl) {
	l.Logger.Level = logrus.Level(level)
}

// Output returns the echo logging output
func (l *echoLogger) Output() io.Writer {
	return l.Writer()
}

// SetOutput sets the echo logging output
func (l *echoLogger) SetOutput(output io.Writer) {
	l.Logger.Logger.Out = output
}

// Prefix returns the echo logging output
func (l *echoLogger) Prefix() string {
	return l.Logger.Data["app"].(string)
}

// SetPrefix returns the echo logging output
func (l *echoLogger) SetPrefix(prefix string) {
	l.Logger.Data["app"] = prefix
}

func loggerMiddlewareHandler(c echo.Context, next echo.HandlerFunc, log *logger.Logger) error {
	req := c.Request()
	res := c.Response()
	start := time.Now()

	err := next(c)
	if err != nil {
		return err
	}
	stop := time.Now()

	p := req.URL.Path
	if p == "" {
		p = "/"
	}

	bytesIn := req.Header.Get(echo.HeaderContentLength)
	if bytesIn == "" {
		bytesIn = "0"
	}

	requestID := req.Header.Get(echo.HeaderXRequestID)

	if requestID == "" {
		requestID = res.Header().Get(echo.HeaderXRequestID)
	}

	log.WithFields(map[string]interface{}{
		"id":            requestID,
		"time_rfc3339":  time.Now().Format(time.RFC3339),
		"remote_ip":     c.RealIP(),
		"host":          req.Host,
		"uri":           req.RequestURI,
		"method":        req.Method,
		"path":          p,
		"referer":       req.Referer(),
		"user_agent":    req.UserAgent(),
		"status":        res.Status,
		"latency":       strconv.FormatInt(stop.Sub(start).Nanoseconds()/1000, 10),
		"latency_human": stop.Sub(start).String(),
		"bytes_in":      bytesIn,
		"bytes_out":     strconv.FormatInt(res.Size, 10),
		"headers":       req.Header,
		"query":         req.URL.Query(),
	}).Info("Handled request")

	return nil
}

func loggerMiddleware(log *logger.Logger) echo.MiddlewareFunc {
	return func(next echo.HandlerFunc) echo.HandlerFunc {
		return func(c echo.Context) error {
			return loggerMiddlewareHandler(c, next, log)
		}
	}
}

func setLogMiddleware(e *echo.Echo, log *logger.Logger) {
	e.Logger = &echoLogger{log}
	e.Pre(loggerMiddleware(log))
}
