package testutil

import (
	"fmt"
	"log"
	"sync"

	"github.com/influxdata/telegraf"
)

var _ telegraf.Logger = &CaptureLogger{}

const (
	LevelError = 'E'
	LevelWarn  = 'W'
	LevelInfo  = 'I'
	LevelDebug = 'D'
	LevelTrace = 'T'
)

type Entry struct {
	Level byte
	Name  string
	Text  string
}

func (e *Entry) String() string {
	return fmt.Sprintf("%c! [%s] %s", e.Level, e.Name, e.Text)
}

// CaptureLogger defines a logging structure for plugins.
type CaptureLogger struct {
	Name     string // Name is the plugin name, will be printed in the `[]`.
	messages []Entry
	sync.Mutex
}

func (l *CaptureLogger) print(msg Entry) {
	l.Lock()
	l.messages = append(l.messages, msg)
	l.Unlock()
	log.Print(msg.String())
}

func (l *CaptureLogger) logf(level byte, format string, args ...any) {
	l.print(Entry{level, l.Name, fmt.Sprintf(format, args...)})
}

func (l *CaptureLogger) loga(level byte, args ...any) {
	l.print(Entry{level, l.Name, fmt.Sprint(args...)})
}

func (*CaptureLogger) Level() telegraf.LogLevel {
	return telegraf.Trace
}

// Adding attributes is not supported by the test-logger
func (*CaptureLogger) AddAttribute(string, interface{}) {}

func (l *CaptureLogger) Errorf(format string, args ...interface{}) {
	l.logf(LevelError, format, args...)
}

func (l *CaptureLogger) Error(args ...interface{}) {
	l.loga(LevelError, args...)
}

func (l *CaptureLogger) Warnf(format string, args ...interface{}) {
	l.logf(LevelWarn, format, args...)
}

func (l *CaptureLogger) Warn(args ...interface{}) {
	l.loga(LevelWarn, args...)
}

func (l *CaptureLogger) Infof(format string, args ...interface{}) {
	l.logf(LevelInfo, format, args...)
}

func (l *CaptureLogger) Info(args ...interface{}) {
	l.loga(LevelInfo, args...)
}

func (l *CaptureLogger) Debugf(format string, args ...interface{}) {
	l.logf(LevelDebug, format, args...)
}

func (l *CaptureLogger) Debug(args ...interface{}) {
	l.loga(LevelDebug, args...)
}

func (l *CaptureLogger) Tracef(format string, args ...interface{}) {
	l.logf(LevelTrace, format, args...)
}

func (l *CaptureLogger) Trace(args ...interface{}) {
	l.loga(LevelTrace, args...)
}

func (l *CaptureLogger) NMessages() int {
	l.Lock()
	defer l.Unlock()
	return len(l.messages)
}

func (l *CaptureLogger) Messages() []Entry {
	l.Lock()
	msgs := make([]Entry, len(l.messages))
	copy(msgs, l.messages)
	l.Unlock()
	return msgs
}

func (l *CaptureLogger) filter(level byte) []string {
	l.Lock()
	defer l.Unlock()
	var msgs []string
	for _, m := range l.messages {
		if m.Level == level {
			msgs = append(msgs, m.String())
		}
	}
	return msgs
}

func (l *CaptureLogger) Errors() []string {
	return l.filter(LevelError)
}

func (l *CaptureLogger) Warnings() []string {
	return l.filter(LevelWarn)
}

func (l *CaptureLogger) LastError() string {
	l.Lock()
	defer l.Unlock()
	for i := len(l.messages) - 1; i >= 0; i-- {
		if l.messages[i].Level == LevelError {
			return l.messages[i].String()
		}
	}
	return ""
}

func (l *CaptureLogger) Clear() {
	l.Lock()
	defer l.Unlock()
	l.messages = make([]Entry, 0)
}
