package logger

import (
	"encoding/json"
	"fmt"
	"io"
	"strings"
	"time"

	"gitlab.com/uafrica/go-utils/errors"
)

type ILogger interface {
	Fatalf(format string, args ...interface{})
	Fatal(args ...interface{})
	Errorf(format string, args ...interface{})
	Error(args ...interface{})
	Warnf(format string, args ...interface{})
	Warn(args ...interface{})
	Infof(format string, args ...interface{})
	Info(args ...interface{})
	Debugf(format string, args ...interface{})
	Debug(args ...interface{})
}

type Logger struct {
	//apiRequest       *events.APIGatewayProxyRequest
	//currentRequestID *string
	level  Level
	writer io.Writer
	data   map[string]interface{}
}

func (l Logger) WithFields(data map[string]interface{}) Logger {
	newLogger := Logger{
		level:  l.level,
		writer: l.writer,
		data:   map[string]interface{}{},
	}
	for n, v := range l.data {
		newLogger.data[n] = v
	}
	for n, v := range data {
		newLogger.data[n] = v
	}
	return newLogger
}

func (l Logger) Fatalf(format string, args ...interface{}) {
	l.WithFields(map[string]interface{}{"call_stack": errors.Stack(3)}).log(LevelFatal, 1, fmt.Sprintf(format, args...))
}

func (l Logger) Fatal(args ...interface{}) {
	l.WithFields(map[string]interface{}{"call_stack": errors.Stack(3)}).log(LevelFatal, 1, fmt.Sprint(args...))
}

func (l Logger) Errorf(format string, args ...interface{}) {
	l.WithFields(map[string]interface{}{"call_stack": errors.Stack(3)}).log(LevelError, 1, fmt.Sprintf(format, args...))
}

func (l Logger) Error(args ...interface{}) {
	l.WithFields(map[string]interface{}{"call_stack": errors.Stack(3)}).log(LevelError, 1, fmt.Sprint(args...))
}

func (l Logger) Warnf(format string, args ...interface{}) {
	l.log(LevelError, 1, fmt.Sprintf(format, args...))
}

func (l Logger) Warn(args ...interface{}) {
	l.log(LevelError, 1, fmt.Sprint(args...))
}

func (l Logger) Infof(format string, args ...interface{}) {
	l.log(LevelInfo, 1, fmt.Sprintf(format, args...))
}

func (l Logger) Info(args ...interface{}) {
	l.log(LevelInfo, 1, fmt.Sprint(args...))
}

func (l Logger) Debugf(format string, args ...interface{}) {
	l.log(LevelDebug, 1, fmt.Sprintf(format, args...))
}

func (l Logger) Debug(args ...interface{}) {
	l.log(LevelDebug, 1, fmt.Sprint(args...))
}

func (l Logger) log(level Level, skip int, msg string) {
	if level <= l.level && l.writer != nil {
		entry := Entry{
			Timestamp: time.Now(),
			Level:     level,
			Caller:    errors.GetCaller(skip + 2).Info(),
			Data:      l.data,
			Message:   strings.ReplaceAll(msg, "\n", ";"),
		}

		// jsonEntry, err := json.Marshal(entry)
		// if err != nil {
		// 	l.writer.Write([]byte(fmt.Sprintf("failed to marshal entry: %v: %+v\n", err, entry)))
		// }
		// l.writer.Write(append(jsonEntry, []byte("\n")...))

		source := fmt.Sprintf("%s(%d)", entry.Caller.File, entry.Caller.Line)
		if len(source) > 25 {
			source = source[len(source)-25:]
		}
		textEntry := fmt.Sprintf("%s %5.5s %-25.25s %s",
			entry.Timestamp.Format("2006-01-02 15:04:05"),
			entry.Level,
			source,
			entry.Message)
		if len(entry.Data) > 0 {
			jsonData, _ := json.Marshal(entry.Data)
			textEntry += " " + string(jsonData)
		}
		l.writer.Write(append([]byte(textEntry), []byte("\n")...))
	}
}

type Entry struct {
	Timestamp time.Time              `json:"time"`
	Level     Level                  `json:"level"`
	Caller    errors.CallerInfo      `json:"caller"`
	Data      map[string]interface{} `json:"data"`
	Message   string                 `json:"message"`
}

// func sendRaygunError(fields map[string]interface{}, errToSend error) {
// 	if os.Getenv("DEBUGGING") == "true" {
// 		// Don't log raygun errors on debug
// 		return
// 	}

// 	raygun, err := RaygunReporter()
// 	if err != nil || raygun == nil {
// 		logger.Errorf("Unable to create Raygun client: " + err.Error())
// 		return
// 	}

// 	env := getEnvironment()
// 	tags := []string{env}
// 	//todo: raygun.Version(globals.BuildVersion)
// 	//todo: tags = append(tags, globals.BuildVersion)

// 	if logger.apiRequest != nil {
// 		methodAndPath := logger.apiRequest.HTTPMethod + ": " + logger.apiRequest.Path
// 		tags = append(tags, methodAndPath)
// 		fields["body"] = logger.apiRequest.Body
// 		fields["query"] = logger.apiRequest.QueryStringParameters
// 		fields["identity"] = logger.apiRequest.RequestContext.Identity
// 	}

// 	raygun.Tags(tags)
// 	if logger.currentRequestID != nil {
// 		fields["request_id"] = logger.currentRequestID
// 	}

// 	fields["env"] = env
// 	raygun.CustomData(fields)
// 	raygun.Request(fakeHttpRequest())

// 	if errToSend == nil {
// 		errToSend = errors.New("")
// 	}
// 	err = raygun.SendError(errToSend)
// 	if err != nil {
// 		logger.Errorf("Failed to send raygun error: " + err.Error())
// 	}
// }

// func fakeHttpRequest() *http.Request {
// 	if logger.apiRequest == nil {
// 		return nil
// 	}

// 	requestURL := url.URL{
// 		Path: logger.apiRequest.Path,
// 		Host: logger.apiRequest.Headers["Host"],
// 	}
// 	request := http.Request{
// 		Method: logger.apiRequest.HTTPMethod,
// 		URL:    &requestURL,
// 		Header: logger.apiRequest.MultiValueHeaders,
// 	}
// 	return &request
// }

// func RaygunReporter() (*raygun4go.Client, error) {
// 	key := "AwdpHhwOF1lTT6AppyEHA"

// 	// TODO Raygun per environment
// 	//env := getEnvironment()
// 	//if env == "dev" {
// 	//	key = "q98iTyE5CcF7raZsIiLA"
// 	//} else if env == "stage" {
// 	//	key = "TA54mzcv9cBWBLmfwIQMDg"
// 	//} else if env == "prod" {
// 	//	key = "BXdraqiPKBXImqP4siK1w"
// 	//}

// 	raygun, err := raygun4go.New("uAfrica V3", key)
// 	if err != nil || raygun == nil {
// 		logger.Errorf("Unable to create Raygun client:" + err.Error())
// 		return nil, err
// 	}

// 	return raygun, nil
// }