package output

import (
	"context"
	"fmt"
	"io"
	"log/slog"
)

// the log level for exploits.
var exploitLevel = LevelStatus

// Sets the log level for exploit logging. Anything below the provided value will not get logged.
func SetExploitLogLevel(level slog.Level) {
	exploitLevel = level
}

// reset logger to the appropriate level / output location and write the log.
func doExploitLog(descriptor io.Writer, level slog.Level, msg string, keys ...any) {
	logMutex.Lock()
	defer logMutex.Unlock()

	if level >= exploitLevel {
		logger := resetLogger(descriptor, exploitLevel)

		ctx := context.Background()
		logger.Log(ctx, level, msg, keys...)
	}
}

// PrintfTrace formats according to a format specifier and logs as TRACE
// If the exploit is not logging to file, this will go to standard error.
func PrintfTrace(format string, msg ...interface{}) {
	PrintTrace(fmt.Sprintf(format, msg...))
}

// PrintTrace logs a string as TRACE
// If the exploit is not logging to file, this will go to standard error.
func PrintTrace(msg string, keys ...any) {
	doExploitLog(stdErrDesc, LevelTrace, msg, keys...)
}

// PrintfDebug formats according to a format specifier and logs as DEBUG
// If the exploit is not logging to file, this will go to standard error.
func PrintfDebug(format string, msg ...interface{}) {
	PrintDebug(fmt.Sprintf(format, msg...))
}

// PrintDebug logs a string as TRACE
// If the exploit is not logging to file, this will go to standard error.
func PrintDebug(msg string, keys ...any) {
	doExploitLog(stdErrDesc, LevelDebug, msg, keys...)
}

// PrintfStatus formats according to a format specifier and logs as STATUS (aka INFO)
// If the exploit is not logging to file, this will go to standard out.
func PrintfStatus(format string, msg ...interface{}) {
	PrintStatus(fmt.Sprintf(format, msg...))
}

// PrintStatus logs a string as STATUS (aka INFO)
// If the exploit is not logging to file, this will go to standard out.
func PrintStatus(msg string, keys ...any) {
	doExploitLog(stdOutputDesc, LevelStatus, msg, keys...)
}

// PrintfWarn formats according to a format specifier and logs as WARN
// If the exploit is not logging to file, this will go to standard error.
func PrintfWarn(format string, msg ...interface{}) {
	PrintWarn(fmt.Sprintf(format, msg...))
}

// PrintWarn logs a string as WARN
// If the exploit is not logging to file, this will go to standard error.
func PrintWarn(msg string, keys ...any) {
	doExploitLog(stdErrDesc, LevelWarning, msg, keys...)
}

// PrintVersion logs a string as VERSION
// If the exploit is not logging to file, this will go to standard output.
func PrintVersion(msg string, host string, port int, version string) {
	// update the db with the extracted version
	doExploitLog(stdOutputDesc, LevelVersion, msg, "host", host, "port", port, "version", version)
}

// PrintfSuccess formats according to a format specifier and logs as SUCCESS
// If the exploit is not logging to file, this will go to standard out.
func PrintfSuccess(format string, msg ...interface{}) {
	PrintSuccess(fmt.Sprintf(format, msg...))
}

// PrintStatus logs a string as SUCCESS
// If the exploit is not logging to file, this will go to standard out.
func PrintSuccess(msg string, keys ...any) {
	doExploitLog(stdOutputDesc, LevelSuccess, msg, keys...)
}

// PrintfError formats according to a format specifier and logs as ERROR
// If the exploit is not logging to file, this will go to standard error.
func PrintfError(format string, msg ...interface{}) {
	PrintError(fmt.Sprintf(format, msg...))
}

// PrintError logs a string as ERROR
// If the exploit is not logging to file, this will go to standard error.
func PrintError(msg string, keys ...any) {
	doExploitLog(stdErrDesc, LevelError, msg, keys...)
}
